[Powered by Google Translate] [Afsnit 5 - Mere behagelig] [Rob Bowden - Harvard University] [Dette er CS50. - CS50.TV] Som jeg sagde i min e-mail, er der en masse ting, du kan bruge andet end apparatet til rent faktisk at gøre problemet sæt. Vi anbefaler, at du gør det i maskinen, bare fordi så kan vi lettere hjælpe dig og vi ved, hvordan alting kommer til at arbejde. Men som et eksempel på, hvor du kan gøre ting, hvis sige, du ikke har adgang til til et apparat eller du ønsker at arbejde i Science Center kælderen - som de faktisk har apparatet også - hvis du ønsker at arbejde hvor som helst. Et eksempel er har du set / hørt af SSH? SSH er dybest set ligesom forbinde til noget. Faktisk lige nu er jeg SSHed ind i apparatet. Jeg har aldrig arbejde direkte i apparatet. Her er apparatet, og hvis du kigger ned her kan du se denne IP-adresse. Jeg har aldrig arbejdet i selve apparatet; Jeg kommer altid hen til en iTerm2 vindue / terminalvindue. Du kan SSH til at IP-adressen, ssh jharvard@192.168.129.128. Jeg husker, at antallet meget nemt, fordi det er sådan en nice mønster. Men det vil spørge mig om min adgangskode, og nu er jeg i apparatet. Dybest set, på dette tidspunkt, hvis du har åbnet en terminal inde i selve apparatet, dette interface, men du ville bruge det, er nøjagtig det samme som grænsefladen jeg bruger herovre, men nu er du SSHed. Du behøver ikke at SSH til apparatet. Et eksempel på et andet sted du kunne SSH til, er jeg temmelig sikker på at du har som standard - Oh. Bigger. Alle jer burde have som standard FAS konti på de ansvarlige for bedriftsrådgivningsordningen servere. For mig, ville jeg SSH til rbowden@nice.fas.harvard.edu. Det kommer til at bede dig om, at den første gang, og du siger ja. Min adgangskode er bare at være min FAS password. Og så nu, jeg SSHed til nice servere, og jeg kan gøre noget, jeg vil have på her. En masse af klasser du kan tage, ligesom 124, vil have dig uploade ting til her til rent faktisk at indsende dit problem sæt. Men siger du ikke har adgang til dit apparat. Så kan du gøre ting, som på her det vil sige - Dette er blot vores afsnit med spørgsmål. Den vil bede dig om at gøre dette i apparatet. I stedet vil jeg bare gøre det på serveren. Jeg har tænkt mig at lyne det. Problemet vil være, at du er vant til at bruge noget som gedit eller hvad indersiden af ​​apparatet. Du kommer ikke til at have den på FAS-serveren. Det hele er bare at være denne tekst interface. Så du kan enten en, så prøv at lære en tekst editor, som de har. De har Nano. Nano er normalt ret nemt at bruge. Du kan bruge dine pile og skrive normalt. Så det er ikke svært. Hvis du ønsker at få virkelig fancy, kan du bruge Emacs, som jeg burde nok ikke have åbnet, da jeg ikke engang ved, hvordan man lukke Emacs. Kontrol X, Control C? Yeah. Eller du kan bruge Vim, hvilket er hvad jeg bruger. Og så dem er dine muligheder. Hvis du ikke ønsker at gøre det, kan du også, hvis man ser på manual.cs50.net-- Oh. På en pc, kan du SSH bruger PuTTY, som du bliver nødt til at hente separat. På en Mac, kan du bare som standard bruge Terminal eller du kan downloade iTerm2, der er som et rart, fancy Terminal. Hvis du går til manual.cs50.net kan du se et link til Notepad + +, hvilket er hvad du kan bruge på en pc. Det giver dig mulighed SFTP fra Notepad + +, som er dybest set SSH. Hvad dette vil lade dig gøre, er at redigere dine filer lokalt, og derefter når du ønsker at gemme dem, vil det spare på nice.fas, hvor du kan derefter køre dem. Og tilsvarende på en Mac bliver TextWrangler. Så det lader dig gøre det samme. Det lader dig redigere filer lokalt og gemme dem til nice.fas, hvor du kan derefter køre dem. Så hvis du nogensinde sidder fast uden et apparat, har du disse muligheder til stadig gøre dit problem sæt. Det ene problem vil være, at du ikke vil have den CS50 bibliotek fordi nice.fas ikke som standard have det. Du kan enten downloade den CS50 bibliotek - Jeg tror ikke jeg har brug for, at der på dette punkt. Du kan enten downloade den CS50 biblioteket og kopiere den over til nice.fas, eller jeg tror på dette punkt har vi ikke bruge det længere alligevel. Eller hvis vi gør det, kan du for øjeblikket, erstatte det med de implementeringer af funktionerne i CS50 biblioteket alligevel. Så det burde ikke være så meget af en begrænsning. Og det er det. Jeg vil gå tilbage til apparatet nu, vi vil gøre alt i apparatet. Ser man på vores afsnit med spørgsmål i begyndelsen, ligesom jeg sagde i min e-mail, vi er nødt til at tale om en kort du skulle se. Vi har omdirigere & Rør og disse tre spørgsmål. Til hvilken stream gør funktioner som printf skrive som standard? Så stream. Hvad er en strøm? En strøm er dybest set ligesom det er bare nogle - Det er ikke engang en kilde til 1s og 0'erne. Strømmen det er at bede om her er standard ud. Og så standard ud er en strøm, at når du skriver til det, det vises på skærmen. Standard ud, ved åen, betyder det du bare skrive 1s og 0'erne til det, og den anden ende af standard ud bare læser fra denne stream. Det er bare en perlerække af 1s og 0'erne. Du kan skrive til streams, eller du kan læse fra vandløb afhængigt af hvad strømmen faktisk er. De to andre standard strømme er standard i og standard fejl. Standard i er, når du gør GetString, det venter på at input stuff. Så det venter på dig, er det faktisk venter på standard ind, som er virkelig, hvad du får, når du skriver på tastaturet. Du skriver i standard i. Standardfejl svarer grundlæggende til standard ud, men det er specialiseret i at når du udskriver til standard fejl, du skulle kun udskrive fejlmeddelelser til at så du kan skelne mellem almindelige meddelelser udskrives til skærmen versus fejlmeddelelser, afhængigt af, om de gik til standard ud eller standard fejl. Filer også. Standard ud, standard i, og standard error er blot særlige streams, men virkelig enhver fil, når du åbner en fil, bliver den en strøm af bytes hvor du kan bare læse fra denne stream. Du, for det meste, kan bare tænke på en fil som en strøm af bytes. Så hvad vandløb skal de skrive til som standard? Standard ud. Hvad er forskellen mellem> og >>? Har nogen se videoen på forhånd? Okay. > Vil være, hvordan du omdirigerer til filer, og >> også vil omdirigere output til filer, men det er i stedet kommer til at tilføje til filen. For eksempel sige, lad os jeg tilfældigvis har dict lige her, og den eneste ting indersiden af ​​dict er kat, kat, hund, fisk, hund. En kommando, som du har på kommandolinjen er kat, der er bare at udskrive hvad der er i en fil. Så når jeg siger kat dict, går det at udskrive kat, kat, hund, fisk, hund. Det er alt kat gør. Det betyder, at det trykte til standard ud kat, kat, hund, fisk, hund. Hvis jeg i stedet ønsker at omdirigere det til en fil, kan jeg bruge> og kanalisere den til, hvad filen er. Jeg ringer filen fil. Så nu hvis jeg ls, vil jeg se, at jeg har en ny fil kaldet fil. Og hvis jeg åbner det op, det kommer til at have præcis, hvad kat sat på kommandolinjen. Så nu hvis jeg gør det igen, så det kommer til at omdirigere outputtet i filen, og jeg har tænkt mig at have nøjagtig de samme ting. Så teknisk set er det helt tilsidesatte hvad vi havde. Og vi vil se, om jeg skifter dict, jeg tog ud hund. Nu, hvis vi kat dict i filen igen, vi vil have den nye version med hund fjernet. Så det fuldstændigt tilsidesætter den. I stedet, hvis vi bruger >>, går det at tilføje fil. Nu åbner en fil, vi se, vi har bare det samme udskrives to gange fordi det var der én gang, så vi vedlagt originalen. Så det er hvad> og >> gør. Er den næste spørge - Det spørger ikke om det. Den anden, som vi har, er <, som hvis> omdirigerer standard ud, du gør 2>, der er omdirigere standardfejl. Så hvis noget gik til standard fejl, ville det ikke blive sat i txt2. Men bemærk hvis jeg gør 2>, så er det stadig udskrive Hej, Rob! til kommandolinjen fordi jeg kun omdirigere standardfejl, jeg ikke omdirigere standard ud. Standardfejl og standard ud er forskellige. Hvis du ville faktisk skrive til standard fejl, så jeg kunne ændre dette for at være fprintf til stderr. Så printf, som standard, udskriver til standard ud. Hvis jeg ønsker at udskrive til standard fejl manuelt, så jeg er nødt til at bruge fprintf og angive, hvad jeg vil udskrive til. Hvis man i stedet gjorde jeg fprintf stdout, så det er dybest set svarer til printf. Men fprintf til standard fejl. Så nu, hvis jeg kanalisere denne ind i txt2, Hej, Rob! stadig få trykt på kommandolinjen da det bliver udskrevet til standard fejl, og jeg er kun omdirigere standard ud. Hvis jeg nu omdirigere standard fejl, nu det ikke bliver udskrevet, og txt2 bliver Hej, Rob! Så nu kan du printe dine egentlige fejl til standard fejl og printe dine almindelige meddelelser til standard ud. Og så når du kører dit program, kan du køre det som. / Hej denne type med 2> så din program kommer til at køre normalt, men eventuelle fejlmeddelelser, du får, kan du tjekke senere i din fejllog, så fejl, og derefter se senere, og din fejl fil vil have eventuelle fejl, der skete. Spørgsmål? Den sidste er rør, som du kan tænke på som at tage standarden ud fra en kommando og derfor er dette det standard i den næste kommando. Et eksempel her er echo er en kommandolinje ting der er bare at gentage, hvad jeg sætter som sit argument. Jeg vil ikke sætte anførselstegn. Echo blah, blah, blah er bare at udskrive blah, blah, blah. Før, da jeg sagde, at jeg var nødt til at sætte Rob ind i en txt-fil fordi jeg kun kan omdirigere txt-filer i stedet, / hvis jeg gentager Rob og derefter rør det til. / hej, vil det også gøre det samme slags ting. Dette tager outputtet fra denne kommando, echo Rob, og bruge det som input til. / hej. Du kan tænke på det som første omdirigering echo Rob ind i en fil og derefter input til. / hej denne fil, der lige er udsendt. Men det tager den midlertidige fil ud af billedet. Spørgsmål om det? Det næste spørgsmål kommer til at involvere dette. Hvad rørledningen kunne du bruge til at finde antallet af unikke navne i en fil kaldet names.txt? Kommandoerne vi kommer til at ønsker at bruge her er unikke, så uniq, og derefter wc. Du kan gøre mennesket uniq til rent faktisk at se på, hvad der gør, og det er bare at filtrere tilstødende matchende linjer fra indgangen. Og mennesket wc kommer til at udskrive ny linje, ord og byte for hver fil. Og det sidste, vi vil ønsker at bruge er sortering, som vil bare sortere linjer af txt-fil. Hvis jeg gør nogle txt-fil, names.txt, og det er Rob, Tommy, Joseph, Tommy, Joseph, RJ, Rob, hvad jeg ønsker at gøre her, er at finde antallet af unikke navne i denne fil. Så hvad bør svaret være? >> [Studerende] 4. >> Yeah. Det bør være 4 siden Rob, Tommy, Joseph, RJ er de eneste entydige navne i denne fil. Det første skridt, hvis jeg bare ord tæller på names.txt, dette er faktisk fortæller mig alt. Dette er faktisk udskrivning - lad os se, mand wc - newlines, ord og byte tæller. Hvis jeg kun bekymre sig om linjer, så jeg kan bare gøre wc-l names.txt. Så det er trin 1. Men jeg ønsker ikke at wc-l names.txt fordi names.txt bare indeholder alle de navne, og jeg ønsker at filtrere alle ikke-unikke dem. Så hvis jeg gør uniq names.txt, betyder det ikke helt give mig, hvad jeg vil fordi de duplikerede navne er der stadig. Hvorfor er det? Hvorfor er uniq ikke gør, hvad jeg vil? [Studerende] Den gentagede ikke [uhørlige] >> Yeah. Husk manden side for uniq siger filter tilstødende matchende linjer. De er ikke tilstødende, så det vil ikke filtrere dem. Hvis jeg sortere dem først, sort names.txt vil sætte alle de dublerede linjer sammen. Så nu sortere names.txt er det. Jeg har tænkt mig at ønsker at bruge det som input til uniq, der er | uniq. Det giver mig Joseph, RJ, Rob, Tommy, og jeg vil gerne bruge det som input til wc-l, som vil give mig 4. Ligesom der står her, hvad kunne pipeline du bruge? Du kan gøre en masse ting som at bruge en række kommandoer hvor du bruger output fra en kommando som input til den næste kommando. Du kan gøre en masse ting, en masse smarte ting. Spørgsmål? Okay. Det er det for rør og omdirigering. Nu skal vi gå videre til den faktiske ting, den kodende ting. Inde i denne PDF, vil du se denne kommando, og du vil få lyst til at køre denne kommando i dit apparat. wget er kommandoen for bare at få noget fra internettet, dybest set, så wget og denne webadresse. Hvis du gik til denne URL i din browser, vil den downloade denne fil. Jeg har lige klikket på den, så den hentede fil for mig. Men skrivning wget af den ting inde i terminalen er bare at hente det ind i din terminal. Jeg har section5.zip, og du vil få lyst til at lyne section5.zip, som vil give dig en mappe kaldet section5, som vil have alle de filer, vi kommer til at bruge i dag inde i den. Da disse programmers filnavne antyder, de er en smule buggy, så din mission er at finde ud af hvorfor bruger gdb. Skal alle dem downloades / ved hvordan man får dem downloadet i deres apparat? Okay. Løb ./buggy1 vil det sige Segmentation fault (core dumpet), som helst du får en segfault, det er en dårlig ting. Under hvilke omstændigheder får du en segfault? [Studerende] dereference en null-pointer. >> Yeah. Så er et eksempel. Dereferere en null-pointer du kommer til at få en segfault. Hvad en segfault betyder, at du rører hukommelse, bør du ikke røre. Så dereferere en null-pointer rører adresse 0, og dybest set, alle computere i dag sige, at adressen 0 er hukommelse, bør du ikke røre. Så det er derfor dereferere en null-pointer resulterer i en segfault. Når du tilfældigvis ikke initialisere en pegepind, så det har en garbage værdi, og så når du forsøger at dereference det, efter al sandsynlighed, du rører hukommelse det er midt i himlen. Hvis du tilfældigvis til at få heldige og garbage værdi sket til at pege på et sted på stakken eller noget, så når du dereference at pointer, som du ikke har initialiseret, intet vil gå galt. Men hvis det er at pege på, siger, et sted mellem stakken og bunke, eller det peger bare til et sted, der ikke er blevet brugt af dit program endnu, så er du rører hukommelse du skal ikke røre, og du segfault. Når du skriver en rekursiv funktion, og det recurses for mange gange og din stack bliver for stor, og de stak kolliderer ind i ting at det ikke bør kolliderede med, er du rører hukommelse du ikke bør røre, så du segfault. Det er, hvad en segfault er. Det er også af samme grund, at hvis du har en streng som - lad os gå tilbage til det forrige program. I hello.c--Jeg er bare at lave noget andet. char * s = "Hej Verden!" Hvis jeg bruger * s = noget eller s [0] = 'X'; så gør goddag. / hej, hvorfor det segfault? Hvorfor dette segfault? Hvad ville du forvente at ske? Hvis jeg gjorde printf ("% s \ n", s), hvad ville du forvente at blive udskrevet? [Studerende] X hej. >> Yeah. Problemet er, at når du erklærer en streng som denne, s er en pointer, der kommer til at gå på stakken, og hvad s peger på, er denne streng, som er indeholdt i læselageret. Så bare ved navn, read-only memory, bør du få den idé at hvis du forsøger at ændre hvad der er i read-only memory, du laver noget, du ikke bør gøre med hukommelse, og du segfault. Dette er faktisk en stor forskel mellem char * s og char s []. Så char s [], nu denne streng vil blive lagt på stakken, og stakken er ikke skrivebeskyttet, hvilket betyder, at dette bør arbejde helt fint. Og det gør det. Husk, at når jeg gør char * s = "Hej Verden!", S selv er på stakken men s peger på et andet sted, og at et andet sker for at være skrivebeskyttet. Men char s [] er bare noget på stakken. Så det er et andet eksempel på en segfault sker. Vi så, at ./buggy1 resulterede i en segfault. I teorien bør du ikke se på buggy1.c straks. I stedet vil vi se på det gennem gdb. Bemærk, at når du får Segmentation fault (core dumpet), du får denne fil herovre kaldet kerne. Hvis vi ls-l, vil vi se, at kernen er normalt en temmelig stor fil. Dette er antallet af bytes af filen, så det ser ud som det er 250-noget kilobytes. Grunden til dette er, at det, kernen dump faktisk er er, når dit program går ned, status for hukommelse af dit program bare bliver kopieret og indsat i denne fil. Det bliver dumpet i den pågældende fil. Dette program, mens den kørte, tilfældigvis har en hukommelse på omkring 250 kilobyte, og så det er hvad fik dumpet ind i denne fil. Nu kan du se på denne fil, hvis vi gør GDB buggy1 kerne. Vi kan bare gøre gdb buggy1, og det vil bare starte gdb regelmæssigt, hjælp buggy1 som sit input fil. Men hvis du gør gdb buggy1 kerne, så er det specifikt kommer til at starte op gdb ved at se på det centrale fil. Og du siger buggy1 betyder gdb ved, at denne kerne fil kommer fra buggy1 program. Så gdb buggy1 kerne vil straks bringe os til hvor programmet skete for at afslutte. Vi ser her Program afsluttet med signal 11, Segmentation fault. Vi tilfældigvis til at se en linje forsamlingsfrihed, og som sandsynligvis er ikke meget nyttigt. Men hvis du skriver bt eller backtrace, er der vil være den funktion det giver os en liste over vores nuværende stakrammer. Så backtrace. Det ser ud til at vi kun har to stakrammer. Den første er vores vigtigste stakramme, og den anden er den stakramme for denne funktion, at vi tilfældigvis er i, som ligner vi kun har forsamlingen kode til. Så lad os gå tilbage til vores vigtigste funktion, og for at gøre, at vi kan gøre ramme 1, og jeg tror, ​​vi kan også gøre ned, men jeg næsten aldrig gøre ned - eller op. Yeah. Op og ned. Up bringer dig op en stakramme, ned bringer dig ned en stak ramme. Jeg er tilbøjelig til aldrig at bruge det. Jeg har lige specifikt sige ramme 1, som er at gå til rammen mærket 1. Frame 1 kommer til at bringe os ind i main stakramme, og det siger lige her kodelinjen vi tilfældigvis at være på. Hvis vi ønskede et par flere linjer kode, kan vi sige liste, og det kommer til at give os alle de linjer kode omkring det. Den linje, vi segfaulted på var 6: if (strcmp ("CS50 rocks", argv [1]) == 0). Hvis det ikke er indlysende endnu, kan du få det lige herfra blot ved at tænke, hvorfor det segfaulted. Men vi kan tage det et skridt videre og sige, "Hvorfor ville argv [1] segfault?" Lad os print argv [1], og det ser ud som det er 0x0, som er den null-pointer. Vi strcmping CS50 sten og null, og så kommer til at segfault. Og hvorfor er argv [1] null? [Studerende] Fordi vi ikke gav det nogen kommandolinjeargumenter. Yeah. Vi har ikke givet det nogen kommandolinjeargumenter. Så ./buggy1 kun vil have argv [0] være ./buggy1. Det kommer ikke til at have en argv [1], så der kommer til at segfault. Men hvis, i stedet, det gør jeg bare CS50, det vil sige Du får et D fordi det er, hvad det er meningen at gøre. Ser man på buggy1.c, er det meningen at udskrive "Du får en D" - Hvis argv [1] er ikke "CS50 rocks", "Du får en D", ellers "Du får et A!" Så hvis vi ønsker en A, vi har brug for denne for at sammenligne så sandt, hvilket betyder, at det skal sammenlignes med 0. Så argv [1] skal være "CS50 rocks". Hvis du ønsker at gøre det på kommandolinjen, skal du bruge \ at undslippe rummet. Så CS50 \ klipper og du får en A! Hvis du ikke gør det omvendte skråstreg, hvorfor dette ikke virker? [Studerende] Det er to forskellige argumenter. >> Yeah. Argv [1] bliver CS50, og argv [2] vil være sten. Okay. Nu ./buggy2 vil segfault igen. I stedet for at åbne det med sin kerne-fil, vil vi bare åbne buggy2 direkte, så gdb buggy2. Nu, hvis vi bare køre vores program, så det kommer til at sige Program modtagne signal SIGSEGV, som er den segfault signal, og det er her det skete at ske. Ser man på vores backtrace, ser vi, at vi var i funktion oh_no, som blev kaldt af den funktion Dinky, som blev kaldt af funktionen binky, som blev kaldt af main. Vi kan også se de argumenter til disse funktioner. Argumentet til Dinky og Binky var 1. Hvis vi en liste over funktionen oh_no, ser vi, at oh_no bare gør char ** s = NULL; * S = "BOOM"; Hvorfor skulle det mislykkes? [Studerende] Du kan ikke dereference null pointer? >> Yeah. Dette er bare at sige s er NULL, uanset om det sker for at være en char **, som, afhængigt af hvordan du fortolker det, kunne det være en pointer til en pointer til en streng eller et array af strenge. Det er s er NULL, så * s dereferere en null-pointer, og så dette kommer til at gå ned. Dette er en af ​​de hurtigste måder, du kan muligvis segfault. Det er bare at erklære en null-pointer og straks segfaulting. Det er, hvad oh_no gør. Hvis vi går op én ramme, så vi vil komme ind i den funktion, der hedder oh_no. Jeg har brug for at gøre det ned. Hvis du ikke indtaster en kommando, og du bare trykke Enter igen, det vil bare gentage den forrige kommando, du kørte. Vi er i frame 1. Notering denne ramme ser vi her er vores funktion. Du kan ramme liste igen, eller du kan gøre liste 20, og det vil vise mere. Funktionen dinky siger, at hvis jeg er 1, derefter gå til oh_no funktionen, ellers gå til den tætsiddende funktion. Og vi ved, at jeg er 1, fordi vi tilfældigvis til at se heroppe at dinky blev kaldt med argumentet 1. Eller du kan bare printe jeg, og det vil sige jeg er 1. Vi er i øjeblikket i dinky, og hvis vi går op en anden ramme, vi ved, vi vil ende i binky. Up. Nu er vi i binky. Notering denne funktion - listen fra før halv afbrød mig - det startede som om jeg er 0, så vil vi kalde det oh_no, ellers kalde dinky. Vi ved, jeg var 1, så det kaldte dinky. Og nu er vi tilbage i main, og vigtigste er bare at være int i = rand ()% 3; Det er bare at give dig et tilfældigt tal, der er enten 0, 1 eller 2. Det kommer til at kalde binky med dette nummer, og det vil vende tilbage 0. Ser på dette, bare gå gennem programmet manuelt uden at køre med det samme, du ville sætte en pause punkt på main, hvilket betyder, at når vi kører programmet Deres program løber indtil den rammer et knækpunkt. Så kører programmet, vil det køre og så vil det ramme den primære funktion og stopper. Nu er vi inde i main, og trin eller næste vil bringe os til den næste linje kode. Du kan gøre trin eller næste. Hitting næste, nu jeg er sat til rand ()% 3, så vi kan printe værdien af ​​i, og det vil sige jeg er 1. Nu er det ligegyldigt, om vi bruger næste eller trin. Jeg gætter det betød noget i den foregående, men vi ønsker at bruge næste. Hvis vi bruger skridt, vi træder ind i funktionen, hvilket betyder, kig på den faktiske ting der sker inde i binky. Hvis vi bruge næste, så betyder det gå over den funktion og bare gå til den næste linje kode i vores vigtigste funktion. Lige her på denne linje, var jeg på, hvor det sagde rand ()% 3; hvis jeg gjorde skridt, ville det gå i gennemførelsen af ​​rand og se på, hvad der sker der, og jeg kunne gå gennem den rand-funktionen. Men jeg er ligeglad med den rand-funktionen. Jeg vil bare gå til den næste linje kode i main, så jeg bruge næste. Men nu har jeg ligeglad med binky funktion, så jeg ønsker at træde ind i det. Nu er jeg i binky. Den første linje kode vil sige, hvis (i == 0), jeg tager et skridt, vi se, at vi ender på Dinky. Hvis vi liste ting, vi kan se, at det kontrolleres, er i = 0. Jeg er ikke lig med 0, så det gik til andet betingelse, der ringer dinky (i). Du kan blive forvirret. Hvis man bare ser på disse linjer direkte, kan du tror, ​​at hvis (i == 0), okay, så jeg tog et skridt og nu er jeg på Dinky (i), du måske tror det må betyde i = 0 eller sådan noget. Nej, det betyder bare, at det ved det kan holde sig direkte til den linje dinky (i). Fordi jeg ikke er 0, er det næste skridt ikke vil ende på andet. Else er ikke en linje det kommer til at stoppe ved. Det er bare kommer til at gå til næste linje kan det faktisk at gennemføre, hvilket er dinky (i). At træde ind i Dinky (i), ser vi, om (i == 1). Vi kender i = 1, så når vi skridt, vi ved, vi kommer til at ende op i oh_no fordi i = 1 kalder funktionen oh_no, som du kan træde ind, som vil sætte char ** s = til NULL og straks "BOOM". Og så faktisk ser på gennemførelsen af ​​buggy2, dette, er jeg bare at få et tilfældigt tal - 0, 1 eller 2 - calling binky, som, hvis i er 0 den kalder oh_no, ellers kalder Dinky, der kommer op her. Hvis jeg er 1, kald oh_no, ellers kalde slinky, som kommer op her, hvis i er 2, kalder oh_no. Jeg tror ikke engang der er en vej - Er der nogen se en måde at gøre dette et program, der ikke segfault? Fordi medmindre jeg mangler noget, hvis jeg er 0, vil du straks segfault, ellers kan du gå til en funktion, som hvis i er 1 du segfault, ellers kan du gå til en funktion, hvor hvis jeg er 2 du segfault. Så uanset hvad du gør, du segfault. Jeg gætte en måde at ordne det ville være stedet for at gøre char ** s = NULL, du kunne allokere plads til denne streng. Vi kunne gøre malloc (sizeof) - sizeof hvad? [Studerende] (char) * 5? >> Betyder dette synes ret? Jeg antager det vil virke, hvis jeg rent faktisk kørte det, men det er ikke hvad jeg leder efter. Kig på den type s. Lad os tilføje int *, så int * x. Jeg ville gøre malloc (sizeof (int)). Eller hvis jeg ønskede et array af 5, ville jeg gøre (sizeof (int) * 5); Hvad hvis jeg har en int **? Hvad ville jeg malloc? [Elev] Størrelse af markøren. >> Yeah. (Sizeof (int *)); Samme ting hernede. Jeg ønsker (sizeof (char *)); Dette vil afsætte plads til den pointer, peger på "BOOM". Jeg behøver ikke at afsætte plads til "BOOM" i sig selv fordi det er dybest set svarer til, hvad jeg sagde før af char * x = "BOOM". "BOOM" findes allerede. Det sker for at eksistere i read-only region af hukommelsen. Men den allerede findes, hvilket betyder denne linje kode, hvis s er en char **, så * s er en char *, og du indstiller denne char * at pege på "BOOM". Hvis jeg ønskede at kopiere "BOOM" i s, så ville jeg nødt til at afsætte plads til s. Jeg vil gøre * s = malloc (sizeof (char) * 5); Hvorfor 5? Hvorfor ikke 4? Det ser ud som "BOOM" er 4 tegn. >> [Studerende] The null-tegn. Yeah. Alle dine strenge vil få brug for den null-tegn. Nu kan jeg gøre noget som strcat - Hvad er funktionen til at kopiere en streng? [Studerende] CpY? >> Strcpy. mand strcpy. Så strcpy eller strncpy. strncpy er lidt sikrere, da du kan angive præcist, hvor mange tegn, men her er det ligegyldigt, fordi vi kender. Så strcpy og kigge i argumenterne. Det første argument er vores destination. Det andet argument er vores kilde. Vi kommer til at kopiere ind i vores destination * s markøren "BOOM". Hvorfor kan du ønsker at gøre dette med et strcpy i stedet for lige, hvad vi havde før af * s = "BOOM"? Der er en grund du måske ønsker at gøre dette, men hvad er det årsagen? [Studerende] Hvis du ønsker at ændre noget i "BOOM". >> Yeah. Nu kan jeg gøre noget som s [0] = 'X'; fordi s peger på den bunke, og at rummet på heapen at s peger på er en pegepind til mere plads på heapen, som er lagring "BOOM". Så denne kopi af "BOOM" bliver gemt i bunke. Der er teknisk set to kopier af "BOOM" i vores program. Der er den første, som er bare givet af denne "BOOM" strengen konstant, og den anden kopi af "BOOM", strcpy skabte kopi af "BOOM". Men kopi af "BOOM" bliver lagret på bunke, og den bunke du er fri for at skifte. Den bunke er ikke skrivebeskyttet, så det betyder, at s [0] vil lade dig ændre værdien af ​​"BOOM". Det kommer til at lade dig ændre disse tegn. Spørgsmål? Okay. Flytning til buggy3, lad os gdb buggy3. Vi bare køre det, og vi ser vi får en segfault. Hvis vi backtrace, er der kun to funktioner. Hvis vi går op i vores vigtigste funktion, ser vi, at vi segfaulted på denne linje. Så bare at kigge på denne linje, int linie = 0 for (; fgets denne ting ikke er lig med NULL; line + +). Vores tidligere ramme hed _IO_fgets. Du vil se, at en masse med indbygget i C-funktioner, at når du får den segfault, vil der være rigtig kryptiske funktionsnavne som denne _IO_fgets. Men det kommer til at forholde sig til dette fgets opkald. Et eller andet sted inde her, vi segfaulting. Hvis vi ser på de argumenter til fgets, kan vi udskrive buffer. Lad os udskrive som en - Åh, nej. Print kommer ikke til at fungere præcis som jeg ønsker det. Lad os se på det aktuelle program. Buffer er et tegn array. Det er et tegn array af 128 tegn. Så når jeg siger udskriftsbufferen, går det at udskrive disse 128 tegn, som jeg gætte er, hvad der forventes. Hvad jeg var på udkig efter, er udskrive adresse buffer, men det betyder ikke rigtig fortælle mig meget. Så når jeg tilfældigvis sige op her x buffer, det viser mig 0xbffff090, som, hvis du husker fra tidligere eller andet tidspunkt, Oxbffff tendens til at være en stabel-ish region. Stakken tendens til at starte et sted lige under 0xc000. Bare ved at se denne adresse ved jeg, at buffer sker på stakken. Genoptages mit program, løbe, op, buffer vi så var denne sekvens af tegn der er temmelig meget meningsløs. Så udskrivning fil, hvad fil ud? [Studerende] Null. >> Yeah. Filen er en af ​​typen FILE *, så det er en pointer, og værdien af ​​denne pegepind er null. Så fgets vil forsøge at læse fra denne pegepind på en indirekte måde, men for at få adgang til denne pegepind, det skal dereference det. Eller, for at få adgang til, hvad det skal pege på, det dereferences det. Så det er dereferere en null-pointer og det segfault'er. Jeg kunne have genstartet det der. Hvis vi bryder vores vigtigste punkt og køre, den første linje kode er char * filename = "nonexistent.txt"; Det burde give en temmelig stor vink om, hvorfor dette program mislykkes. Indtastning næste bringer mig til næste linje, hvor jeg åbner denne fil, og så er jeg straks komme ind i vores linje, hvor der engang jeg ramte næste, går det til segfault. Er der nogen ønsker at smide en grund til, at vi måske segfaulting? [Studerende] Filen findes ikke. >> Yeah. Dette formodes at være en antydning at når du åbner en fil, du har brug for at kontrollere, at filen rent faktisk eksisterer. Så her, "nonexistent.txt"; Når vi fopen filnavn til læsning, vi så nødt til at sige if (fil == NULL) og sige printf ("Filen findes ikke!" eller - endnu bedre - filename) tilbagevenden 1; Så nu tjekker vi for at se, om det er NULL før der rent faktisk fortsætter og forsøger at læse fra den pågældende fil. Vi kan lave den bare for at se, at det virker. Jeg skal indbefatte en ny linje. Så nu nonexistent.txt findes ikke. Du bør altid tjekke for den slags ting. Du bør altid tjekke om fopen returnerer NULL. Du bør altid tjekke at sikre, at malloc ikke returnerer NULL, ellers kan du segfault. Nu buggy4.c. Løb. Jeg kan gætte dette venter på input eller eventuelt uendelig løkke. Ja, det er uendelig løkke. Så buggy4. Det ser ud til vi er uendelig løkke. Vi kan knække ved main, køre vores program. I gdb, så så længe forkortelsen du bruger, er entydig eller særlige forkortelser, som de leverer til dig, så kan du bruge n til at bruge næste i stedet for at skulle skrive ud næste hele vejen. Og nu hvor jeg har ramt n gang, kan jeg bare trykke Enter for at holde i gang næste stedet for at skulle slå n Indtast, n Enter, n Enter. Det ser ud som jeg er i en slags for-løkke, der er fastsættelsen array [i] til 0. Det ser ud som jeg aldrig jeg bryde ud af dette for-løkke. Hvis jeg udskriver i, så jeg er 2, så vil jeg gå næste. Jeg vil printe jeg, jeg er 3, så vil jeg gå næste. Jeg vil printe i, og jeg er 3. Næste, printe jeg, jeg er 4. Faktisk print sizeof (array), så størrelsen af ​​tabel er 20. Men det ser ud som om der er nogle særlige gdb kommando til at gå indtil der sker noget. Det er ligesom at sætte en betingelse på værdien af ​​den variable. Men jeg kan ikke huske hvad det er. Så hvis vi holder gang - Hvad sagde du? Hvad tog du op? [Studerende] ikke udviser jeg tilføje - >> Yeah. Så vise jeg kan hjælpe. Hvis vi bare vise i, vil det lægge op her, hvad værdien af ​​i er så jeg behøver ikke at printe det ud hver gang. Hvis vi bare holde går næste, vi ser 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5. Noget går galt, og jeg er ved at blive nulstillet til 0. Ser man på buggy4.c, ser vi alt, hvad der sker, er int array [5]; for (i = 0; i <= sizeof (array), i + +) array [i] = 0; Hvad kan vi se, at der er galt her? Som et tip, da jeg lavede gdb buggy4 - lad os bryde main, run - Jeg gjorde print sizeof (array) bare for at se, hvad den betingelse er, hvor jeg skal endelig bryde ud. Hvor er jeg? Har jeg køre? Jeg har ikke erklære endnu. Så print sizeof (array), og det er 20, som forventes siden min array er af størrelse 5, og det er af 5 heltal, så det hele ting bør være 5 * sizeof (int) bytes, hvor sizeof (int) tendens til at være 4. Så sizeof (array) er 20. Hvad skal det være? [Studerende] divideret med sizeof (int). >> Ja, / sizeof (int). Det ser ud som om der er stadig et problem her. Jeg tror, ​​det skal bare være < da det er stort set altid > [Bowden] Ja. Når vi går ud over slutningen af ​​vores array, en eller anden måde dette rum, at vi altovervejende er tvingende værdien af ​​i. Og så hvis vi ser på buggy4, bryde main, køre, lad os udskrive adresse i. Det ser ud til det er bffff124. Lad os nu udskrive adressen på array [0]. 110. Hvad med [1]? 114. [2] 118. 11c, 120. array [5] er bfff124. Så array [5] har samme adresse som jeg, hvilket betyder, at array [5] i. Hvis de har samme adresse, de er de samme ting. Så når vi sætter array [5] til 0, sætter vi i til 0. Og hvis du synes om dette i form af stablen, int i er erklæret først, hvilket betyder, at jeg får noget plads på stakken. Så array [5] er allokeret, så derefter 20 byte fordeles på stakken. Så jeg bliver tildelt først, derefter disse 20 bytes bliver tildelt. Så jeg sker lige før array, og på grund af den måde, som jeg sagde i sidste uge, hvor det er teknisk stakken vokser ned, når man indeks i et array, er vi garanteret, at 0. position i arrayet altid sker før den første position i opstillingen. Det er en slags hvordan jeg trak det i sidste uge. Bemærk, at i bunden har vi adressen 0 og øverst har vi adresse Max. Stakken vokser konstant ned. Lad os sige, vi tildeler jeg. Vi afsætter heltal i, hvilket betyder, lad os bare sige op her heltal i bliver tildelt. Så vi tildeler vores array af 5 heltal, hvilket betyder, at under det, da stakken vokser ned, får de 5 heltal tildelt. Men på grund af hvordan arrays fungerer, er vi garanteret, at den første position i arrayet altid adressen mindre end den anden ting i arrayet. Så array-position 0 altid skal ske først i hukommelsen, hvorimod array-position 1 skal ske efter det og array position 2 skal ske efter det, hvilket betyder, at array-position 0 ville ske et eller andet sted hernede, matrix position 1 ville ske ovenfor, fordi at bevæge sig op betyder højere adresser siden den maksimale adresse er heroppe. Så array [0] hernede, array [1] heroppe, array [2] op her, array [3] op her. Bemærk, hvordan før vi tildelt heltal i hele vejen op her, som vi bevæger os længere og længere ind i vores array, får vi tættere og tættere på vores heltal i. Det er bare sådan, at array [5], hvilket er en position uden for vores array, er præcis, hvor heltal jeg tilfældigvis skal tildeles. Så det er det punkt, hvor vi tilfældigvis rammer plads på stakken der blev afsat til heltal i, og vi sætte det til 0. Det er sådan det fungerer. Spørgsmål? Yeah. [Studerende] Pyt. Okay. [Studerende] Hvordan undgår man den slags fejl? Disse slags fejl? Brug ikke C som dit programmeringssprog. Brug et sprog, der har tabelgrænsekontrollering. Så længe du er forsigtig, du skal bare undgå at gå forbi grænserne for dit array. [Studerende] Så her, når vi gik forbi grænserne for dit array - [Bowden] Det er, hvor tingene begynder at gå galt. >> [Studerende] Åh, okay. Så længe du holder sig inden for hukommelse der er allokeret til dit array, du er fint. Men C gør ingen fejlkontrol. Hvis jeg gør array [1000], vil det gerne bare ændre uanset hvad der sker - Det går til begyndelsen af ​​array, så det går 1000 stillinger efter og indstiller den til 0. Det gør ikke noget kontrol af, at åh, betyder det faktisk ikke have 1000 ting i det. 1000 er langt ud over hvad jeg skal ændre, hvorimod Java eller noget du får vifte out of bounds-indeks eller indeks out of bounds undtagelse. Det er derfor en masse af højere niveau sprog har disse ting hvor hvis du går ud over grænserne for den array, du ikke så du kan ikke ændre tingene nedefra dig og derefter går det meget værre end blot at få en undtagelse sige, at du gik ud over enden af ​​arrayet. [Studerende] Og så skulle vi lige har ændret <= til bare > [Bowden] Yeah. Det bør være > [Studerende] Right. Flere spørgsmål? Okay. [Studerende] Jeg har et spørgsmål. >> Yeah. [Studerende] Hvad er den aktuelle array-variabel? [Bowden] Som hvad er array? Array selv er et symbol. Det er bare adressen på starten af ​​de 20 bytes, som vi refererer til. Du kan tænke på det som en pegepind, men det er en konstant pegepind. Så snart tingene bliver kompileret, er den variable opstilling ikke eksisterer længere. [Studerende] Så hvordan virker det finde størrelsen af ​​array? Størrelsen af ​​tabellen refererer til størrelsen af ​​den pågældende blok, at dette symbol vedrører. Når jeg gør noget som printf ("% p \ n", array); lad os køre den. Hvad gjorde jeg bare forkert? Array 'array' erklæret her. Åh, heroppe. Klang er klog, og det sker for at opdage, at jeg erklærede array som 5 elementer men jeg indeksere i position 1000. Det kan gøre det, fordi disse er blot konstanter. Det kan kun gå så langt i at lægge mærke til, at jeg har tænkt mig ud over rammerne for opstillingen. Men bemærk før, når vi havde jeg være forkert, det kan umuligt afgøre, hvor mange værdier, jeg kunne tage på, så det kan ikke bestemme, at jeg skulle ud over enden af ​​matrixen. Det er bare Dunk at være dygtig. Men nu gør buggy4. Så hvad ellers gør jeg forkert? Implicit erklære bibliotek funktionen 'printf'. Jeg vil ønsker at # include . Okay. Nu kører buggy4. Udskrivning af værdien af ​​array som jeg gjorde her, udskrive den som en pegepind prints noget, der ligner dette - bfb8805c - hvilket er noget adresse der er i stakken-ish region. Array selv er som en pegepind, men det er ikke et virkeligt pointer, da en regelmæssig pointer, vi kan ændre. Array er blot nogle konstant. De 20 blokke af hukommelse starter ved adresse 0xbfb8805c. Så bfb8805c gennem denne adresse +20--eller jeg gætte -20 - er hele den hukommelse, der er afsat til dette array. Array, er variablen i sig selv ikke gemt nogen steder. Når du kompilerer, compileren - hånd bølge ved det - men compileren vil bare bruge hvor det ved opstilling at være. Det ved, hvor denne matrix starter, og så kan det altid bare gøre tingene i form af forskydninger fra begyndelsen. Det behøver ikke en variabel i sig selv til at repræsentere array. Men når jeg gør noget som int * p = array, nu p er en pointer, som peger på, at array, og nu p faktisk eksisterer på stakken. Jeg er fri til at ændre p. Jeg kan gøre p = malloc. Så det oprindeligt pegede på opstilling, og nu peger på noget plads på den bunke. Jeg kan ikke gøre vifte = malloc. Hvis Dunk er klog, vil det råbe ad mig ret off the bat. Faktisk er jeg temmelig sikker på gcc ville gøre dette også. Så array-type 'int [5]' er ikke overdrages. Du kan ikke tildele noget til en tabeltype fordi matrix er bare en konstant. Det er et symbol, som referencer de 20 bytes. Jeg kan ikke ændre det. [Studerende] Og hvor er størrelsen af ​​array opbevaret? [Bowden] Det er ikke gemt nogen steder. Det er, når det er kompilering. Så hvor er størrelsen af ​​arrayet opbevaret? Du kan kun bruge sizeof (array) indersiden af ​​funktion, array er erklæret sig selv. Så hvis jeg gør nogle funktion, foo, og jeg gør (int array []) printf ("% d \ n", sizeof (array)); og derefter ned her kalder jeg foo (array); indersiden af ​​denne funktion - lad os køre den. Dette er Dunk være dygtig igen. Det fortæller mig, at sizeof på array-funktion parameter vil returnere størrelse af 'int * «. Det ville være en fejl, hvis det ikke er, hvad jeg ønskede at ske. Lad os faktisk slukke Werror. Advarsel. Advarsler er fint. Det vil stadig indsamle så længe det har en advarsel. . / A.out vil udskrive 4. Den advarsel, der blev genereret er en klar indikator for, hvad der gik galt. Denne int array bare at udskrive sizeof (int *). Selv hvis jeg sætter array [5] her, er det stadig bare at udskrive sizeof (int *). Så så snart du passerer det ind i en funktion, at sondringen mellem arrays og pointers er ikke-eksisterende. Dette sker for at være et array, der blev erklæret på stakken, men så snart vi passerer denne værdi, at 0xbf blah, blah, blah ind i denne funktion, så er denne pointer peger på, at arrayet på stakken. Så det betyder, at sizeof kun gælder i den funktion, at den matrix blev erklæret, hvilket betyder, at når du kompilerer denne funktion, når Dunk går gennem denne funktion, den ser array er en int array af størrelse 5. Så det ser sizeof (array). Tja, det er 20. Det er faktisk hvordan sizeof dybest set virker for næsten alle tilfælde. Sizeof er ikke en funktion, det er en operatør. Du behøver ikke ringe til sizeof funktion. Sizeof (int), vil compileren bare oversætte det til 4. Forstået? Okay. [Studerende] Så hvad er forskellen mellem sizeof (array) i hoved-og i Foo? Dette skyldes, at vi siger sizeof (array), som er af typen int *, henviser til, at arrayet ned her er ikke af typen int *, det er en int array. [Studerende] Så hvis du havde parameter i array [] i stedet for int * array, ville det betyde, at du stadig kunne ændre array, fordi nu er det en pointer? [Bowden] Kan du lide det? >> [Studerende] Yeah. Kan du ændre opstilling i funktionen nu? [Bowden] Du kan ændre array i begge tilfælde. I begge disse tilfælde er du velkommen til at sige array [4] = 0. [Studerende] Men kan du gøre array-point til noget andet? [Bowden] Oh. Yeah. I begge tilfælde - >> [studerende] Yeah. [Bowden] Sondringen mellem array [] og en int * array, der er ingen. Du kan også få nogle flerdimensional tabel i her for nogle bekvem syntaks, men det er stadig bare en pegepind. Det betyder, at jeg er fri til at gøre opstilling = malloc (sizeof (int)), og nu peger et andet sted. Men ligesom hvordan det fungerer for evigt og altid, ændre dette array ved at gøre det peger på noget andet ændrer ikke dette array ned her, fordi det er en kopi af argumentet, det er ikke en pointer til dette argument. Og faktisk, ligesom flere tegn på, at det er præcis det samme - vi allerede har set, hvad trykning array-prints - hvad hvis vi printer adressen på arrayet eller adressen på adressen på arrayet til enten af ​​dem? Lad os se bort fra denne ene. Okay. Det er fint. Det er nu kører. / A.out. Trykning array, derefter trykning af adressen på arrayet, er det samme. Array bare ikke eksisterer. Det ved, hvornår du udskriver array, er du udskriver det symbol, der henviser til de 20 bytes. Udskrivning af adressen på arrayet, ja, går matrix ikke. Det har ikke en adresse, så den netop udskriver adressen på de 20 bytes. Så snart du kompilere ned, gerne i din kompileret buggy4. / A.out, matrix er ikke-eksisterende. Pointers eksisterer. Arrays ikke. Blokkene af hukommelsen repræsenterer sættet stadig eksisterer, men den variable array og variable af denne type ikke eksisterer. Det er ligesom de vigtigste forskelle mellem arrays og pointers er, så snart du foretager funktionskald, er der ingen forskel. Men inde i den funktion, at den matrix selv er erklæret arbejder sizeof anderledes idet du udskriver størrelsen af ​​blokke i stedet for størrelsen af ​​den type, og du kan ikke ændre det, fordi det er et symbol. Udskrivning af ting og adressen på den ting udskriver det samme. Og det er temmelig meget det. [Studerende] Kan du sige det en gang til? Jeg kunne have overset noget. Udskrivning array og adresse på arrayet udskriver det samme, hvorimod hvis du udskriver en pointer i forhold til adressen på markøren, den ene ting udskriver adressen på hvad du peger på, den anden udskriver adressen på markøren på stakken. Du kan ændre en pegepind, og du kan ikke ændre et array symbol. Og sizeof pointer kommer til at udskrive størrelsen af ​​denne pointer type. Så int * p sizeof (p) vil udskrive 4, men int array [5] print sizeof (array) kommer til at udskrive 20. [Studerende] Så int array [5] vil udskrive 20? >> Ja. Det er derfor inde i buggy4 når det plejer at være sizeof (array) dette gjorde i <20, som er ikke, hvad vi ønskede. Vi ønsker i <5. >> [Studerende] Okay. [Bowden] Og så så snart du begynder at passerer i de funktioner, hvis vi gjorde int * p = array; indersiden af ​​denne funktion, kan vi dybest set bruge p og array i nøjagtig samme måde, bortset fra sizeof problem og den skiftende problem. Men p [0] = 1, er det samme som at sige array [0] = 1; Og så snart vi siger foo (array), eller foo (p); indersiden af ​​Foo funktion, er dette det samme opkald to gange. Der er ingen forskel mellem disse to opkald. Alle godt på det? Okay. Vi har 10 minutter. Vi vil forsøge at komme igennem denne Hacker Typer program, denne hjemmeside, som kom ud sidste år eller sådan noget. Det er bare skulle være som du skriver tilfældigt, og det udskrives - Uanset fil det sker for at have læsset er, hvad det ser ud som om du skriver. Det ligner en slags styresystem kode. Det er, hvad vi ønsker at gennemføre. Du bør have en binær eksekverbar navn hacker_typer der tager i et enkelt argument, filen til "hacker type." Kørsel den eksekverbare bør rydde skærmen og derefter udskrive et tegn fra de beståede-in-filen, hver gang brugeren trykker på en tast. Så uanset tast du trykker på, skal det smide væk og i stedet udskrive en karakter fra filen der er argumentet. Jeg vil stort set fortælle dig, hvad de ting, vi vil få brug for at vide er. Men vi ønsker at tjekke termios biblioteket. Jeg har aldrig brugt dette bibliotek i hele mit liv, så det har meget minimale formål. Men det vil være på biblioteket, vi kan bruge til at smide det tegn, du ramte når du skriver ind i standard i. Så hacker_typer.c, og vi vil gerne # include . Ser man på manden side for termios - jeg gætte det er terminal OS eller noget - Jeg ved ikke, hvordan det skal læses. Ser man på dette, siger det at medtage disse 2 filer, så vi vil gøre det. Første ting først, vi ønsker at tage i et enkelt argument, som er den fil, vi skal åbne. Så hvad vil jeg gøre? Hvordan kan jeg kontrollere at se, at jeg har et enkelt argument? [Studerende] Hvis argc lig det. >> [Bowden] Yeah. Så hvis (argc = 2!) Printf ("Brug:% s [fil for at åbne]"). Så nu hvis jeg køre dette uden at give et andet argument - åh, jeg har brug for den nye linje - du vil se det siger brug:. / hacker_typer, og derefter det andet argument bør være den fil jeg vil åbne. Nu hvad gør jeg? Jeg ønsker at læse fra denne fil. Hvordan læser jeg fra en fil? [Studerende] Du åbner det første. >> Yeah. Så fopen. Hvad betyder fopen ud? [Elev] Filename. >> [Bowden] Filename bliver argv [1]. [Studerende] Og hvad du vil gøre med det, så det - >> [Bowden] Yeah. Så hvis du ikke kan huske, kan du bare gøre mennesket fopen, hvor det vil være en const char * sti hvor sti er filnavn, const char * mode. Hvis du tilfældigvis ikke huske, hvad mode er, så kan du kigge efter mode. Inde i manualsider, er det skråstreg hvad du kan bruge til at søge efter ting. Så jeg skriver / mode for at søge efter mode. n og N er, hvad du kan bruge til at cykle gennem de søgeord kampe. Her står det argument tilstand peger på en streng begyndende med en af ​​følgende sekvenser. Så r, Open tekstfil til læsning. Det er, hvad vi ønsker at gøre. Til læsning, og jeg vil gemme det. De ting kommer til at være en fil *. Nu hvad vil jeg gøre? Giv mig et sekund. Okay. Nu hvad vil jeg gøre? [Studerende] Check om det er NULL. >> [Bowden] Yeah. Hver gang du åbner en fil, så sørg for at du er med held i stand til at åbne den. Nu vil jeg gøre det termios ting, hvor jeg vil først læse mine aktuelle indstillinger og gemme dem til noget, så jeg ønsker at ændre mine indstillinger at smide noget tegn, jeg skriver, og så vil jeg opdatere disse indstillinger. Og så ved afslutningen af ​​programmet, vil jeg gerne skifte tilbage til mine oprindelige indstillinger. Så struct bliver af typen termios, og jeg har tænkt mig at have to af dem. Den første vil være mine current_settings, og så de kommer til at være mine hacker_settings. Først, jeg lyst til at gemme mine aktuelle indstillinger, så jeg har tænkt mig at ønsker at opdatere hacker_settings, og derefter vej i slutningen af ​​mit program, jeg vil vende tilbage til de aktuelle indstillinger. Så sparer aktuelle indstillinger, den måde der virker, vi mand termios. Vi ser, at vi har denne int tcsetattr, int tcgetattr. Jeg passere i en termios struct ved sin pointer. Den måde det vil se er - jeg har allerede glemt hvad funktionen blev kaldt. Kopier og indsæt det. Så tcgetattr, så jeg ønsker at videregive i struct, at jeg gemmer oplysningerne i, der bliver current_settings, og det første argument er file descriptor for de ting, jeg ønsker at gemme attributter. Hvad file descriptor er er ligesom enhver tid du åbner en fil, det får en file descriptor. Når jeg fopen argv [1], det får en file descriptor, som du henviser til når du ønsker at læse eller skrive til den. Det er ikke den file descriptor jeg vil bruge her. Der er tre arkivbeskrivere du har som standard, som er standard i, standard ud, og standardfejlen. Som standard, tror jeg, det er standard i er 0, standard ud er 1, og standard error er 2. Så hvad gør jeg ønsker at ændre indstillingerne for? Jeg ønsker at ændre indstillingerne for hver gang jeg ramte en karakter, Jeg vil have det til at smide karakter væk i stedet for at udskrive det på skærmen. Hvad stream - standard i, standard ud, eller standardfejlen - reagerer på ting, når jeg skriver på tastaturet? >> [Studerende] Standard i. >> Yeah. Så jeg kan enten gøre 0 eller jeg kan gøre stdin. Jeg får current_settings af standard i. Nu vil jeg opdatere disse indstillinger, så først vil jeg kopiere ind hacker_settings hvad mine current_settings er. Og hvordan struct arbejde er det vil bare kopiere. Dette kopierer alle de felter, som du ville forvente. Nu vil jeg opdatere nogle af felterne. Ser man på termios, ville du nødt til at læse igennem en masse af denne bare for at se, hvad du ønsker at se efter, men flagene, du vil ønsker at søge er ekko, så ECHO Echo input tegn. Først vil jeg indstille - jeg har allerede glemt hvad felterne er. Dette er, hvad den struct ser ud. Så inputtilstande jeg tror, ​​at vi vil ændre. Vi vil se på en løsning for at sikre, det er hvad vi ønsker at ændre. Vi ønsker at ændre lflag for at forhindre at skulle se igennem alle disse. Vi ønsker at ændre lokale modes. Du ville have til at læse igennem hele denne ting at forstå, hvor alt hører at vi ønsker at ændre. Men det er inde i lokale tilstande, hvor vi kommer til at ønsker at ændre det. Så hacker_settings.cc_lmode er hvad det hedder. c_lflag. Det er her vi kommer ind i bitvise operatører. Vi er slags ud af tid, men vi vil gå igennem det virkelig hurtig. Det er her vi kommer ind bitvise operatører, hvor jeg tror, ​​jeg sagde en gang for længe siden, at når du begynde at behandle med flag, du skal bruge bitvis operatør en masse. Hver bit i flaget svarer til en slags opførsel. Så her, dette flag har en masse forskellige ting, hvor de alle betyder noget andet. Men hvad jeg ønsker at gøre, er bare at slukke for bit, som svarer til ECHO. Så for at slå funktionen fra jeg gøre & = ¬ ECHO. Jeg tror faktisk, det er ligesom Techo eller noget. Jeg skal bare for at tjekke igen. Jeg kan termios det. Det er bare ECHO. ECHO vil være en enkelt bit. ¬ ECHO kommer til at betyde alle bit er sat til 1, hvilket betyder, at alle flag er sat til sand bortset fra ECHO bit. Ved at ende mine lokale flag med dette, betyder det, alle flag, der i øjeblikket er indstillet til sand vil stadig blive sat til sand. Hvis min ECHO flag er sat til sand, så dette er nødvendigvis indstillet til FALSE på ECHO flag. Så denne linje kode bare slukker ECHO flag. De andre linjer kode, vil jeg bare kopiere dem af hensyn til tid og derefter forklare dem. I løsningen, sagde han 0. Det er nok bedre til eksplicit at sige stdin. Bemærk, at jeg også laver ECHO | ICANON her. ICANON henviser til noget separat, hvilket betyder kanoniske tilstand. Hvad kanoniske tilstand betyder som regel, når du skriver ud kommandolinjen, standard i ikke behandler noget, før du rammer newline. Så når du GetString, du skriver en masse ting, så du rammer newline. Det er, når den er sendt til standard i. Det er standard. Når jeg slukker kanoniske mode, nu hver eneste tegn, du trykker er, hvad der bliver behandlet, som normalt er slags dårlig, fordi det er længe om at behandle disse ting, hvilket er hvorfor det er godt at buffer det i hele linjer. Men jeg vil hver karakter skal behandles da jeg ikke vil have det til at vente på mig at ramme newline før den behandler alle de tegn, jeg har at skrive. Dette slår kanoniske tilstand. Denne ting bare betyder, når det rent faktisk behandler tegn. Det betyder, behandle dem straks, så snart jeg skriver dem, bearbejde dem. Og det er den funktion, som opdaterer mine indstillinger for standard i, og TCSA midler at gøre det lige nu. De andre muligheder er vente alt, hvad der er i øjeblikket på strøm er behandlet. Det betyder ikke rigtig noget. Lige nu ændre mine indstillinger til at være, hvad der er i øjeblikket i hacker_typer_settings. Jeg tror, ​​jeg kaldte det hacker_settings, så lad os ændre det. Ændre alt til hacker_settings. Nu ved afslutningen af ​​vores program vil vi ønsker at vende tilbage til hvad der for tiden inde i normal_settings, som vil bare ligne & normal_settings. Bemærk, at jeg har ikke ændret nogen af ​​mine normal_settings siden oprindeligt at få det. Så bare at ændre dem tilbage, jeg passerer dem tilbage i slutningen. Det var opdateringen. Okay. Nu inde i her vil jeg lige forklare koden til gavn for tiden. Det er ikke så meget kode. Vi ser vi læse et tegn fra filen. Vi kaldte det f.. Nu kan du mand fgetc, men hvordan fgetc kommer til at arbejde er bare det kommer til at returnere det tegn, du lige har læst eller EOF, som svarer til slutningen af ​​filen eller en fejl sker. Vi looping, fortsætter med at læse et enkelt tegn fra filen, indtil vi har kørt ud af tegn at læse. Og mens vi gør det, vi venter på et enkelt tegn fra standard i. Hver eneste gang du skriver noget på kommandolinjen, der er læsning i en karakter fra standard i. Derefter putchar er bare at sætte den char vi læser op her fra filen til standard ud. Du kan mennesket putchar, men det er bare at sætte til standard ud, er det udskriver tegn. Du kunne også bare gøre printf ("% c", c) Samme idé. Det kommer til at gøre størstedelen af ​​vores arbejde. Den sidste ting vi vil ønsker at gøre, er bare fclose vores fil. Hvis du ikke fclose, det er en hukommelsesfejl. Vi ønsker at fclose den fil, vi oprindeligt åbnet, og jeg tror, ​​det er det. Hvis vi gør det, jeg allerede har problemer. Lad os se. Hvad gjorde det klage over? Forventede 'int' men argument er af typen 'struct _IO_FILE *'. Vi vil se, om det virker. Kun tilladt i C99. Augh. Okay, gør hacker_typer. Nu får vi mere nyttige beskrivelser. Så brug af sort identifikator «normal_settings«. Jeg kaldte det ikke normal_settings. Jeg kaldte det current_settings. Så lad os ændre alt dette. Nu passerer argument. Jeg vil gøre dette 0 for nu. Okay. . / Hacker_typer cp.c. Jeg heller ikke rydde skærmen i begyndelsen. Men du kan se tilbage på det sidste problem sæt for at se, hvordan du rydder skærmen. Det er bare at udskrive nogle tegn mens dette gør, hvad jeg vil gøre. Okay. Og tænke over, hvorfor dette skulle være 0 i stedet for stdin, som bør # define 0, dette er klager over, at - Før da jeg sagde, at der er arkivbeskrivere men så skal du også have din fil *, en file descriptor er blot et enkelt heltal, hvorimod en FILE * har en hel masse ting forbundet med det. Årsagen til at vi er nødt til at sige 0 i stedet for stdin er, at stdin er en fil *, som peger mod ting, der refererer file descriptor 0. Så selv op her, når jeg gør fopen (argv [1], jeg får en FIL * tilbage. Men et eller andet sted i denne fil * er en ting, der svarer til file descriptor for den pågældende fil. Hvis man ser på den mand side for åben, så jeg tror, ​​du bliver nødt til at gøre mennesket 3 open - nej - mand 2 åben - ja. Hvis man ser på siden for åben, åben er ligesom et lavere niveau fopen, og det er tilbage den faktiske file descriptor. fopen gør en masse ting på toppen af ​​åbne, som i stedet for at returnere bare at file descriptor returnerer en hel FILE * pointer inde i som er vores lille file descriptor. Så standard i refererer til den fil * ting, hvorimod 0 refererer til bare file descriptor standard i sig selv. Spørgsmål? [Griner] blæste gennem det. Ok. Vi er fćrdige. [Griner] [CS50.TV]