[Powered by Google Translate] [§ 5 - Mer komfortabelt] [Rob Bowden - Harvard University] [Dette er CS50. - CS50.TV] Som jeg sa i min e-post, er det mange ting du kan bruke annet enn apparatet til å faktisk gjøre problemet sett. Vi anbefaler at du gjør det i skapet bare fordi da kan vi lettere hjelpe deg og vi vet hvordan alt skal fungere. Men som et eksempel på hvor du kan gjøre ting hvis, sier, trenger du ikke har tilgang et apparat eller du ønsker å jobbe i Science Center kjelleren - som faktisk har de apparatet også - Hvis du ønsker å jobbe hvor som helst. Et eksempel er at du har sett / hørt om SSH? SSH er i utgangspunktet akkurat som å koble til noe. Egentlig akkurat nå er jeg SSHed inn i apparatet. Jeg har aldri arbeide direkte i apparatet. Her er apparatet, og hvis du ser ned her ser du denne IP-adressen. Jeg har aldri jobbe i selve apparatet; Jeg kommer alltid bort til en iTerm2 vindu / terminal-vinduet. Du kan SSH til at IP-adressen, jharvard@192.168.129.128 ssh. Jeg husker at antallet veldig lett fordi det er slik et fint mønster. Men det vil spørre meg om passordet mitt, og nå er jeg i apparatet. I utgangspunktet, på dette punktet, hvis du åpnet opp en terminal inne i selve apparatet, dette grensesnittet, men du vil bruke det, er nøyaktig det samme som grensesnitt jeg bruker over her, men nå er du SSHed. Du trenger ikke å SSH til apparatet. Et eksempel på et annet sted du kunne SSH til er jeg ganske sikker på at du har som standard - Oh. Større. Alle dere bør ha som standard FAS kontoer på FAS servere. For meg, ville jeg SSH til rbowden@nice.fas.harvard.edu. Det kommer til å spørre deg at den første gangen, og du sier ja. Mitt passord er bare kommer til å være min FAS passord. Og så nå er jeg SSHed til hyggelig servere, og jeg kan gjøre hva jeg vil på her. Mange klasser du kan ta, som 124, kommer til å ha deg laste opp ting til her å faktisk sende inn oppgavesett. Men si at du ikke har tilgang til maskinen din. Så kan du gjøre ting, som på her vil det si - Dette er bare vår del av spørsmålene. Det vil be deg om å gjøre dette i apparatet. I stedet vil jeg bare gjøre det på serveren. Jeg kommer til å pakke det. Problemet kommer til å være som du er vant til å bruke noe sånt som gedit eller hva innsiden av apparatet. Du kommer ikke til å ha det på FAS-serveren. Alt er bare kommer til å være denne tekstgrensesnittet. Så du kan enten ett, kan du prøve å lære en tekst editor som de har. De har Nano. Nano er vanligvis ganske enkelt å bruke. Du kan bruke pilene og skriv normalt. Så det er ikke vanskelig. Hvis du ønsker å få virkelig fancy du kan bruke Emacs, som jeg sannsynligvis ikke burde ha åpnet fordi jeg ikke engang vet hvordan du lukker Emacs. Kontroll X, Control C? Ja. Eller du kan bruke Vim, som er det jeg bruker. Og så de er dine alternativer. Hvis du ikke ønsker å gjøre det, kan du også, hvis du ser på manual.cs50.net-- Oh. På en PC kan du SSH bruker PuTTY, som du er nødt til å laste ned separat. På en Mac, kan du bare standard bruk Terminal eller kan du laste ned iTerm2, som er som en hyggelig, fancy Terminal. Hvis du går til manual.cs50.net vil du se en kobling til Notepad + +, som er hva du kan bruke på en PC. Den lar deg SFTP fra Notepad + +, som er utgangspunktet SSH. Hva dette vil la deg gjøre er redigere filene dine lokalt, og deretter når du vil lagre dem, vil det spare til nice.fas, Der kan du kjøre dem. Og tilsvarende på en Mac kommer til å være TextWrangler. Så det lar deg gjøre det samme. Den lar deg redigere filer lokalt og lagre dem til nice.fas, Der kan du kjøre dem. Så hvis du noen gang fast uten et apparat, har du disse alternativene å fortsatt gjøre dine oppgavesett. Den ene problemet kommer til å være at du ikke kommer til å ha CS50 bibliotek fordi nice.fas ikke har som standard at. Du kan enten laste ned CS50 bibliotek - Jeg tror ikke jeg trenger det på dette punktet. Du kan enten laste ned CS50 biblioteket og kopiere den over til nice.fas, eller jeg tror på dette punktet vi ikke bruker det lenger uansett. Eller hvis vi gjør det, kan du foreløpig erstatte den med implementeringer av funksjonene i CS50 biblioteket uansett. Så det bør ikke være så mye av en begrensning. Og det er det. Jeg vil gå tilbake til apparatet nå, vi vil gjøre alt i apparatet. Ser på vår del av spørsmålene, i begynnelsen, som jeg sa i min e-post, Vi må snakke om en kort du skulle se. Vi har omdirigere & Rør og disse tre spørsmålene. Til hvilken stream vil funksjoner som printf skrive som standard? Så stream. Hva er en strøm? En strøm er i utgangspunktet som det er bare noen - Det er ikke engang en kilde 1s og 0s. Strømmen den spør etter her er standard ut. Og så standard ut er en bekk som når du skriver til den, det vises på skjermen. Standard ut, med strøm, betyr det at du bare skrive 1s og 0s til det, og den andre enden av standard ut leser bare fra den datastrømmen. Det er bare en streng av 1s og 0s. Du kan skrive til bekker eller du kan lese fra bekker avhengig av hva strømmen faktisk er. De to andre standard bekker er standard i og standard feil. Standard i er når du GetString, det venter på deg for å legge inn ting. Så det venter på deg, det er faktisk venter på standard i, som er virkelig hva du får når du skriver på tastaturet. Du skriver inn standard i. Standard feil er i utgangspunktet tilsvarer standard ut, men det er spesialisert på at når du skriver ut til standard feil, du skal bare skrive ut feilmeldinger til at slik at du kan skille mellom vanlige meldinger skrevet til skjermen versus feilmeldinger avhengig av om de gikk til standard ut eller standard feil. Filer også. Standard ut, standard i, og standard feil er kun spesielle bekker, men egentlig en fil, når du åpner en fil, blir det en strøm av bytes hvor du kan bare lese fra den datastrømmen. Deg, for det meste, kan bare tenke på en fil som en strøm av bytes. Så hva bekker skriver de til som standard? Standard ut. Hva er forskjellen mellom> og >>? Var det noen som ser videoen på forhånd? Okay. > Kommer til å være hvordan du viderekobler til filer, og >> er også kommer til å omdirigere utdata til filer, men det er i stedet kommer til å føye til filen. For eksempel, la oss si at jeg tilfeldigvis har dict akkurat her, og den eneste ting innsiden av dict er katt, katt, hund, fisk, hund. En kommando som du har på kommandolinjen er katt, som er bare kommer til å skrive hva som er i en fil. Så når jeg sier katt dict, det kommer til å skrive ut katten, katt, hund, fisk, hund. Det er alt katt gjør. Det betyr at det skrives ut til standard ut katten, katt, hund, fisk, hund. Hvis jeg i stedet ønsker å omdirigere det til en fil, kan jeg bruke> og omdirigere den til hva filen er. Jeg ringer filen filen. Så nå hvis jeg ls, jeg ser jeg har en ny fil som heter fil. Og hvis jeg åpner den opp, det kommer til å ha akkurat det katten satt på kommandolinjen. Så nå hvis jeg gjør det igjen, så det kommer til å omdirigere utdataene til fil, og jeg kommer til å ha de samme ting. Så teknisk sett det helt overvurdere hva vi hadde. Og vi får se om jeg endrer dict, tok jeg ut hunden. Hvis vi nå katt dict inn filen på nytt, vi kommer til å ha den nye versjonen med hunden fjernet. Så det overstyrer helt det. I stedet, hvis vi bruker >>, det kommer til å føye fil. Nå åpner filen, ser vi at vi har akkurat det samme trykket to ganger fordi det var der en gang, så vi lagt til den opprinnelige. Så det er hva> og >> gjøre. Spør den neste - Det spør ikke om det. Den andre som vi har er <, som hvis> ruter standard ut, gjør du 2>, som er omdirigere standard feil. Så hvis noe gikk til standard feil, ville det ikke bli satt i txt2. Men legg merke hvis jeg gjør 2>, så er det fortsatt skrive Hei, Rob! i kommandolinjen fordi jeg bare omdirigere standard feil, jeg er ikke omdirigere standard ut. Standard feil og standard ut er forskjellige. Hvis du ønsket å faktisk skrive til standard feil, så jeg kunne endre dette for å være fprintf til standardfeil. Så printf, som standard, skriver til standard ut. Hvis jeg ønsker å skrive ut på standard feil manuelt, så jeg må bruke fprintf og angi hva jeg vil skrive til. Hvis du i stedet jeg gjorde fprintf stdout, så det er i utgangspunktet tilsvarende printf. Men fprintf til standard error. Så nå, hvis jeg omdirigere dette inn txt2, Hello, Rob! er fortsatt å få skrevet ut på kommandolinjen siden det begynner å bli trykt til standard error og jeg bare omdirigere standard ut. Hvis jeg nå omdirigere standard feil, nå er det ikke får skrevet ut, og txt2 kommer til å bli Hei, Rob! Så nå kan du skrive ut dine faktiske feil til standard error og skrive ut vanlige meldinger til standard ut. Og så når du kjører programmet, kan du kjøre det som. / Hallo denne typen med 2> slik at programmet kommer til å kjøre normalt, men eventuelle feilmeldinger som du får kan du sjekke senere i feilloggen, så feil, og deretter se senere og feil fil vil ha noen feil som skjedde. Spørsmål? Den siste er rør, som du kan tenke på som å ta standard ut fra en kommando og gjør det standard i til neste kommando. Et eksempel her er ekko er et kommandolinje ting som bare kommer til å ekko hva jeg satt som argument sin. Jeg vil ikke sette anførselstegn. Echo blah, blah, er blah bare kommer til å skrive ut blah, blah, blah. Før, da jeg sa at jeg måtte sette Rob inn en txt-fil fordi jeg kan bare omdirigere txt-filer, i stedet, / hvis jeg ekko Rob og deretter rør det inn. / hallo, vil det også gjøre det samme type ting. Dette tar produksjonen av denne kommandoen, ekko Rob, og bruke det som input for. / hallo. Du kan tenke på det som første omdirigeringen ekko Rob inn en fil og deretter inn i. / hallo denne filen som nettopp ble generert. Men det tar den midlertidige filen ut av bildet. Spørsmål om det? Det neste spørsmålet kommer til å involvere dette. Hva rørledning kan du bruke til å finne antall unike navn i en fil som heter names.txt? Kommandoene vi kommer til å ønske å bruke her er unike, så Uniq, og deretter wc. Du kan gjøre mennesket uniq å faktisk se på hva som gjør det, og det er bare kommer til å filtrere tilstøtende matchende linjer fra inngangen. Og mannen wc kommer til å skrive ut linjeskift, ord og bytes for hver fil. Og den siste vi kommer til å ønske å bruke er sortering, som kommer til å bare sortere linjene i txt-fil. Hvis jeg gjør noe txt fil, names.txt, og det er Rob, Tommy, Joseph, Tommy, Joseph, RJ, Rob, hva jeg vil gjøre her er å finne antall unike i denne fila. Så hva bør svaret være? >> [Student] 4. >> Ja. Det bør være 4 siden Rob, Tommy, Joseph, RJ er de eneste unike i denne fila. Det første trinnet, hvis jeg bare gjøre ordtelling på names.txt, Dette er faktisk fortelle meg alt. Dette er faktisk utskrift - la oss se, mann wc - linjeskift, ord og byte teller. Hvis jeg bare bryr seg om linjer, så jeg kan bare gjøre wc-l names.txt. Så det er trinn 1. Men jeg ønsker ikke å wc-l names.txt fordi names.txt bare inneholder alle navnene, og jeg ønsker å filtrere ut eventuelle ikke-entydige seg. Så hvis jeg gjør Uniq names.txt, som ikke helt gi meg det jeg vil ha fordi de dupliserte navn er fremdeles der. Hvorfor er det? Hvorfor er uniq ikke gjør hva jeg vil? [Student] The duplikater er ikke [hørbar] >> Ja. Husk mannen siden for uniq sier filter tilstøtende matchende linjer. De er ikke tilstøtende, så det vil ikke filtrere dem. Hvis jeg sortere dem først, er liksom names.txt kommer til å sette alle de dupliserte linjer sammen. Så nå sortere names.txt er det. Jeg kommer til å ønske å bruke det som input til Uniq, som er | uniq. Det gir meg Joseph, RJ, Rob, Tommy, og jeg ønsker å bruke det som input til wc-l, som kommer til å gi meg 4. Som det står her, hva rørledning du bruke? Du kan gjøre mange ting som å bruke en rekke kommandoer der du bruker output fra en kommando som input til neste kommando. Du kan gjøre mange ting, mye smarte ting. Spørsmål? Okay. Det er det for rør og omdirigering. Nå går vi videre til selve ting, koding ting. Innsiden av denne PDF, vil du se denne kommandoen, og du ønsker å kjøre denne kommandoen i apparatet. wget er kommandoen for bare å få noe fra Internett, i utgangspunktet, så wget og denne nettadressen. Hvis du gikk til denne nettadressen i nettleseren din, vil den laste ned denne filen. Jeg bare klikket på den, så det lastet ned filen for meg. Men å skrive wget av at ting inne i terminalen er bare kommer til å laste det ned terminalen. Jeg har section5.zip, og du ønsker å pakke section5.zip, som kommer til å gi deg en mappe som heter section5, som kommer til å ha alle filene vi skal bruke i dag inne i den. Som disse programmenes filnavnene foreslår, er de litt buggy, så din oppgave er å finne ut hvorfor du bruker gdb. Har alle har dem ned / vet hvordan de skal få dem ned i tørketrommelen deres? Okay. Kjører ./buggy1, vil det si Segmentation fault (core dumpet), som noen gang du får en segfault, er det en dårlig ting. Under hvilke omstendigheter får du en segfault? [Student] Dereferencing en nullpeker. >> Ja. Så det er ett eksempel. Dereferencing en nullpeker du kommer til å få en segfault. Hva en segfault betyr er du berører minne du ikke bør berøre. Så dereferencing en nullpeker berører adresse 0, og i utgangspunktet, alle datamaskiner i dag si at 0 er minne du ikke bør berøre. Så det er derfor dereferencing en nullpeker resulterer i en segfault. Når du tilfeldigvis ikke initialisere en peker, da den har en søppel verdi, og så når du prøver å dereference det, i all sannsynlighet du berører minne som er i midten av ingensteds. Hvis du tilfeldigvis ha flaks og søppel verdi skjedd for å peke til et sted på stakken eller noe, så når du dereferanse at pekeren som du ikke har initialisert, ingenting skal gå galt. Men hvis det peker til, si, et sted mellom bunken og haugen, eller den peker bare til et sted som ikke har blitt brukt av programmet ennå, så du berører minne du ikke bør berøre og du segfault. Når du skriver en rekursiv funksjon, og det recurses for mange ganger og stakk vokser for stor og stakken kolliderer inn i ting at det ikke skal være å kollidere med, du berører minne du ikke bør berøre, så du segfault. Det er hva en segfault er. Det er også av samme grunn at hvis du har en streng som - la oss gå tilbake til det forrige programmet. I hallo.c--Jeg bare kommer til å gjøre noe annet. char * s = "Hei, verden!"; Hvis jeg bruker * s = noe eller s [0] = 'X'; så sørg hallo,. / hallo, hvorfor det segfault? Hvorfor har dette segfault? Hva ville du forvente å skje? Hvis jeg gjorde printf ("% s \ n", s), hva ville du forvente å bli skrevet ut? [Student] X hallo. >> Ja. Problemet er at når du deklarerer en streng som dette, s er en peker som kommer til å gå på stakken, og hva er peker til er denne strengen som finnes i read-only minne. Så bare ved navn, read-only minne, bør du få ideen at hvis du prøver å endre hva som er i skrivebeskyttet minne, du gjør noe du ikke bør gjøre med minne og du segfault. Dette er faktisk en stor forskjell mellom char * s og røye s []. Så røye s [], nå denne strengen kommer til å bli satt på stakken, og stakken er ikke skrivebeskyttet, noe som betyr at dette skal fungere helt fint. Og det gjør. Husk at når jeg gjør char * s = "Hei, verden!", S selv på stakken men s peker på et annet sted, og at et annet skjer for å være skrivebeskyttet. Men røye s [] er bare noe på stakken. Så det er et annet eksempel på en segfault skjer. Vi så at ./buggy1 resulterte i en segfault. I teorien skal du ikke se på buggy1.c umiddelbart. I stedet vil vi se på det gjennom gdb. Legg merke til at når du får Segmentation fault (core dumpet), du får denne filen over her kalt kjerne. Hvis vi ls-l, vil vi se at kjernen er vanligvis en ganske stor fil. Dette er antall byte av filen, slik at det ser ut som det er 250-noe kilobyte. Grunnen til dette er at det kjernen dump faktisk er er når programmet krasjer, staten minne programmet bare blir kopiert og limt inn i denne filen. Det blir dumpet i denne filen. Dette programmet, mens det var i gang, har skjedd å ha et minne bruk av rundt 250 kilobyte, og så det er det ble dumpet inn i denne filen. Nå kan du se på denne filen hvis vi gjør gdb buggy1 kjerne. Vi kan bare gjøre gdb buggy1, og det vil bare starte opp gdb regelmessig, bruker buggy1 som sin inndatafilen. Men hvis du gjør gdb buggy1 kjerne, så det er spesielt kommer til å starte opp gdb ved å se på at kjerneinflasjonen fil. Og du sier buggy1 betyr gdb vet at at kjerneinflasjonen filen kommer fra buggy1 programmet. Så gdb buggy1 kjerne kommer til å umiddelbart bringe oss til der programmet skjedde å avslutte. Vi ser her Program avsluttet med 11 signal, Segmentation fault. Vi ser en linje av forsamlingen, som sannsynligvis ikke er veldig nyttig. Men hvis du skriver bt eller tilbakesporing, som kommer til å være funksjonen som gir oss en liste over våre nåværende stack rammer. Så tilbakesporing. Det ser ut som vi bare har to stack rammer. Den første er vår viktigste stabelen ramme, og den andre er stabelen rammen for denne funksjonen som vi måtte være i, som ser ut som vi bare har forsamlingen kode for. Så la oss gå tilbake til vår viktigste funksjon, og å gjøre at vi kan gjøre ramme 1, og jeg tror vi kan også gjøre ned, men jeg nesten aldri gjøre ned - eller opp. Ja. Opp og ned. Opp bringer deg opp en stabel ramme, ned, kommer du ned en stabel ramme. Jeg pleier å aldri bruke det. Jeg bare spesifikt si en ramme, som er å gå til rammen er merket 1. Ramme 1 kommer til å bringe oss inn i hoved stack frame, og det sier her kodelinjen vi måtte være på. Hvis vi ønsket et par flere linjer med kode, kan vi si liste, og det kommer til å gi oss alle linjene med kode rundt den. Linjen vi segfaulted på var 6: if (strcmp ("CS50 rocks", argv [1]) == 0). Hvis det ikke er opplagt ennå, kan du få det rett fra her bare ved å tenke på hvorfor det segfaulted. Men vi kan ta det ett skritt videre og si: "Hvorfor ville argv [1] segfault?" La oss print argv [1], og det ser ut som det er 0x0, som er den nullpeker. Vi strcmping CS50 steiner og null, og så det kommer til å segfault. Og hvorfor er argv [1] null? [Student] Fordi vi ikke gi det noen kommandolinjeargumenter. Ja. Vi gjorde ikke gi det noen kommandolinjeargumenter. Så ./buggy1 er bare nødt til argv [0] være ./buggy1. Det kommer ikke til å ha en argv [1], så det kommer til å segfault. Men hvis, i stedet, jeg bare CS50, det kommer til å si at du får en D fordi det er hva det er ment å gjøre. Ser på buggy1.c, det er ment å skrive ut "Du får et D" - Hvis argv [1] ikke er "CS50 rocks", "Du får et D", ellers "Du får en A!" Så hvis vi ønsker en A, trenger vi denne til å sammenligne som sant, noe som betyr at det kan sammenlignes med 0. Så argv [1] må være "CS50 rocks". Hvis du ønsker å gjøre det på kommandolinjen, må du bruke \ for å unnslippe rommet. Så CS50 \ steiner og du får en A! Hvis du ikke gjør det backslash, hvorfor dette ikke fungerer? [Student] Det er to forskjellige argumenter. >> Ja. Argv [1] kommer til å bli CS50 og argv [2] kommer til å være stein. Okay. Nå ./buggy2 kommer til å segfault igjen. I stedet for å åpne den med sin kjernevirksomhet filen, vil vi bare åpne opp buggy2 direkte, så GDB buggy2. Hvis vi nå bare kjøre programmet vårt, så det kommer til å si Program mottatt signal SIGSEGV, som er segfault signal, og det er her det skjedde skulle skje. Ser på backtrace vår, ser vi at vi var i funksjon oh_no, som ble kalt av funksjonen Eagle, som ble kalt av funksjonen binky, som ble kalt av main. Vi kan også se argumentene til disse funksjonene. Argumentet til Eagle og binky var en. Hvis vi liste funksjonen oh_no, ser vi at oh_no bare gjør char ** s = NULL; * S = "BOOM"; Hvorfor skulle det ikke? [Student] Du kan ikke dereferanse den nullpeker? >> Ja. Dette er bare å si s er NULL, uansett om det skjer for å være en røye **, som, avhengig av hvordan du tolker det, kan det være en peker til en peker til en streng eller en rekke strenger. Det er s er NULL, så * s er dereferencing en nullpeker, og så dette kommer til å krasje. Dette er en av de raskeste måtene du kan muligens segfault. Det er bare å erklære en nullpeker og umiddelbart segfaulting. Det er det oh_no gjør. Hvis vi går opp ett bilde, så vi kommer til å få inn i funksjonen som heter oh_no. Jeg trenger å gjøre det ned. Hvis du ikke skriver en kommando, og du bare trykke Enter igjen, det vil bare gjenta forrige kommandoen du kjørte. Vi er i ramme 1.. Notering denne rammen ser vi her er vår funksjon. Du kan treffe listen igjen, eller du kan gjøre listen 20 og det vil gi mer. Funksjonen dinky sier at hvis jeg er 1, og deretter gå til oh_no funksjon, annet gå til slinky funksjonen. Og vi vet at jeg er en fordi vi måtte se opp her at Eagle ble kalt med argumentet 1. Eller du kan bare skrives ut i, og det vil si jeg er en. Vi er for øyeblikket i Eagle, og hvis vi går opp en annen ramme, vi vet at vi vil ende opp i binky. Opp. Nå er vi i binky. Notering denne funksjonen - listen fra før halvparten kuttet meg - Det startet som om jeg er 0, så vi kommer til å kalle det oh_no, ellers ringer Eagle. Vi vet jeg var en, så det kalles Eagle. Og nå er vi tilbake i main, og viktigste er bare kommer til å være int i = rand ()% 3; Det er bare kommer til å gi deg et tilfeldig tall som er enten 0, 1 eller 2. Det kommer til å ringe binky med det nummeret, og det vil returnere 0. Ser på dette, bare vandre gjennom programmet manuelt uten å kjøre det umiddelbart, du ville sette en pause punkt på main, noe som betyr at når vi kjører programmet programmet går opp til den treffer en pause punkt. Så kjører programmet, vil den kjøre og deretter vil det treffe hovedfunksjon og slutte å kjøre. Nå er vi inne i main, og step eller neste kommer til å bringe oss til neste linje med kode. Du kan gjøre trinn eller neste. Treffer neste, nå jeg har blitt satt til rand ()% 3, slik at vi kan skrive verdien av jeg, og det vil si i er en. Nå er det ingen rolle om vi bruker neste eller trinn. Jeg antar det betydde noe i den forrige, men vi ønsker å bruke neste. Hvis vi bruker trinn, trinn vi inn i funksjonen, noe som betyr titt på den faktiske ting som skjer på innsiden av binky. Hvis vi bruker neste, så det betyr å gå over funksjonen og bare gå til neste linje med kode i våre viktigste funksjon. Her på denne linjen, var jeg på hvor det sto rand ()% 3; hvis jeg gjorde trinn, vil det gå inn i gjennomføringen av rand og se på hva som skjer der, og jeg kunne gå gjennom tilf-funksjonen. Men jeg bryr meg ikke om tilf-funksjonen. Jeg vil bare gå til neste linje med kode i main, så jeg bruker neste. Men nå er jeg bryr meg om den binky funksjon, slik jeg ønsker å gå inn i det. Nå er jeg i binky. Den første linjen med kode skal si if (i == 0), tar jeg et skritt, vi ser vi ender opp på Eagle. Hvis vi liste ting, ser vi at det sjekket er i = 0. Jeg er ikke lik 0, så det gikk til annet tilstand, som kommer til å ringe dinky (i). Du kan bli forvirret. Hvis du bare ser på disse linjene direkte, kan du tenke if (i == 0), okay, så jeg tok et skritt og nå er jeg på dinky (i), du kanskje tror det må bety i = 0 eller noe. Nei, det betyr bare at det vet det kan holde direkte til linjen Eagle (i). Fordi jeg ikke er 0, er neste skritt ikke kommer til å ende på annet. Else er ikke en linje det kommer til å stoppe på. Det er bare kommer til å gå til neste linje kan det faktisk utføre, som er dinky (i). Stepping i dinky (i), ser vi if (i == 1). Vi vet i = 1, så når vi går, vet vi at vi kommer til å ende opp i oh_no fordi i = 1 kaller funksjonen oh_no, som du kan gå inn i, som kommer til å sette char ** s = til NULL og umiddelbart "BOOM". Og deretter faktisk ser på gjennomføringen av buggy2, Dette er jeg bare får et tilfeldig tall - 0, 1 eller 2 - kall binky, som om jeg er 0 kaller oh_no, ellers kaller Eagle, kommer som her oppe. Hvis jeg er en, ring oh_no, ellers kalle slinky som kommer opp her, hvis jeg er to, ring oh_no. Jeg tror ikke engang det er en måte - Ser noen en måte å gjøre dette til et program som ikke vil segfault? Fordi hvis jeg mangler noe, hvis jeg er 0, vil du umiddelbart segfault, annet du gå til en funksjon som hvis jeg er en du segfault, annet du gå til en funksjon der hvis jeg er to segfault deg. Så uansett hva du gjør, segfault deg. Jeg antar en måte å fikse det ville være stedet for å gjøre char ** s = NULL, du kan malloc plass til strengen. Vi kunne gjøre malloc (sizeof) - sizeof hva? [Student] (char) * 5? >> Virker dette riktig? Jeg antar at dette vil fungere dersom jeg faktisk kjørte den, men det er ikke det jeg leter etter. Se på den type s. La oss legge til int *, så int * x. Jeg ville gjøre malloc (sizeof (int)). Eller hvis jeg ønsket en rekke av 5, ville jeg gjøre (sizeof (int) * 5); Hva om jeg har en int **? Hva ville jeg malloc? [Student] Størrelse på pekeren. >> Ja. (Sizeof (int *)); Samme her nede. Jeg ønsker (sizeof (char *)); Dette kommer til å tildele plass for pekeren som peker til "BOOM". Jeg trenger ikke å tildele plass for "BOOM" seg selv fordi dette er i utgangspunktet tilsvarer det jeg sa før av char * x = "BOOM". "BOOM" finnes allerede. Det skjer for å eksistere i read-only regionen minne. Men det finnes allerede, noe som betyr denne linjen med kode, hvis s er en røye **, deretter * s er en char * og du setter denne char * til å peke "BOOM". Hvis jeg ønsket å kopiere "BOOM" i s, så jeg måtte sette av plass for s. Jeg skal gjøre * s = malloc (sizeof (char) * 5); Hvorfor 5? Hvorfor ikke 4? Det ser ut som "BOOM" er 4 tegn. >> [Student] Null karakter. Ja. Alle dine strenger kommer til å trenge null karakter. Nå kan jeg gjøre noe sånt strcat - Hva er funksjonen for kopiering en streng? [Student] CpY? >> Strcpy. Mannen strcpy. Så strcpy eller strncpy. strncpy er litt tryggere siden du kan angi nøyaktig hvor mange tegn, men her er det ingen rolle, fordi vi vet. Så strcpy og se i argumentene. Det første argumentet er vår destinasjon. Det andre argumentet er vår kilde. Vi kommer til å kopiere inn i vår destinasjon * s pekeren "BOOM". Hvorfor kan det være lurt å gjøre dette med en strcpy i stedet for bare hva vi hadde før av * s = "BOOM"? Det er en grunn til at du kanskje ønsker å gjøre dette, men hva er det grunn? [Student] Hvis du ønsker å endre noe i "BOOM". >> Ja. Nå kan jeg gjøre noe som er [0] = 'X'; fordi s peker på haugen, og at plassen på haugen som s peker til er en peker til mer plass på haugen, som er lagring "BOOM". Så dette eksemplaret av "BOOM" blir lagret i haugen. Det er teknisk to kopier av "BOOM" i vårt program. Det er den første som er bare gitt av dette "BOOM" streng konstant, og den andre kopien av "BOOM", skapt strcpy kopien av "BOOM". Men kopien av "BOOM" blir lagret på haugen, og haugen er du fri til å endre. Haugen er ikke skrivebeskyttet, betyr at det er [0] kommer til å la deg endre verdien av "BOOM". Det kommer til å la deg endre disse tegnene. Spørsmål? Okay. Flytte til buggy3, la oss GDB buggy3. Vi bare kjøre den, og vi ser vi få en segfault. Hvis vi backtrace, er det bare to funksjoner. Hvis vi går opp i våre viktigste funksjon, ser vi at vi segfaulted på denne linjen. Så bare å se på denne linjen, int line = 0 for (; fgets denne ting ikke lik NULL; linje + +). Vår forrige rammen ble kalt _IO_fgets. Du vil se at mye med innebygd C-funksjoner, at når du får segfault, vil det være veldig kryptiske funksjon navn som dette _IO_fgets. Men det kommer til å forholde seg til dette fgets samtalen. Et sted inni her, vi segfaulting. Hvis vi ser på argumentene til fgets, kan vi skrive buffer. La oss skrive ut som en - Å, nei. Utskriften er ikke til å fungere akkurat som jeg vil ha det til. La oss se på selve programmet. Buffer er et tegn array. Det er et tegn rekke 128 tegn. Så når jeg sier print buffer, det kommer til å skrive ut de 128 tegn, som jeg tror er det som er forventet. Det jeg var ute etter, er skrive ut adressen buffer, men det betyr ikke fortelle meg virkelig mye. Så når jeg tilfeldigvis si opp her x buffer, viser det meg 0xbffff090, som, hvis du husker fra tidligere eller annet tidspunkt, har en tendens Oxbffff å være en stabel-ish regionen. Stabelen tendens til å begynne et sted i underkant 0xc000. Bare ved å se denne adressen, vet jeg at buffer som skjer på stakken. Starte min program, løpe, opp, buffer vi så var denne sekvensen av tegn som er ganske mye meningsløs. Deretter skrive filen, hva filen se ut? [Student] Null. >> Ja. Filen er en type fil *, så det er en peker, og verdien av at pekeren er null. Så fgets kommer til å prøve å lese fra den peker på en indirekte måte, men for å få tilgang til det pekeren, har det å dereferanse den. Eller, for å få tilgang hva det skal peke til, det dereferences det. Så det dereferencing en nullpeker og det segfaults. Jeg kunne ha startet den der. Hvis vi bryter på våre viktigste punkt og kjøre, den første linjen i koden er char * filename = "nonexistent.txt"; Det burde gi en ganske stor hint om hvorfor dette programmet mislykkes. Skrive neste bringer meg til neste linje, hvor jeg åpner denne filen, og da jeg umiddelbart komme inn vår linje, der en gang jeg traff neste, det kommer til å segfault. Ønsker noen å kaste ut en grunn til at vi kan bli segfaulting? [Student] Filen eksisterer ikke. >> Ja. Dette er ment å være et hint at når du åpner en fil må du sjekke at filen faktisk eksisterer. Så her, "nonexistent.txt"; Når vi fopen filnavn for lesing, vi da trenger å si if (fil == NULL) og si printf ("Filen eksisterer ikke!" eller - enda bedre - filnavn); return 1; Så nå er vi sjekke for å se om det er NULL før du faktisk fortsetter og prøver å lese fra filen. Vi kan remake det bare for å se at det fungerer. Jeg ment å omfatte en ny linje. Så nå nonexistent.txt ikke eksisterer. Du bør alltid sjekke om denne typen ting. Du bør alltid sjekke for å se om fopen returnerer NULL. Du bør alltid sjekke for å være sikker på at malloc ikke returnerer NULL, eller annet du segfault. Nå buggy4.c. Kjører. Jeg gjetter dette venter på innspill eller muligens uendelig looping. Ja, det er uendelig looping. Så buggy4. Det ser ut som vi er uendelig looping. Vi kan bryte på main, kjøre programmet vårt. I gdb, så lenge forkortelsen du bruker er entydig eller spesielle forkortelser som de gir for deg, så kan du bruke n til å bruke neste stedet for å måtte skrive ut neste hele veien. Og nå som jeg har truffet n gang, kan jeg bare trykke Enter for å holde det gående neste i stedet for å måtte treffe n Enter, n Enter n Enter. Det ser ut som jeg er i en slags for loop som er å sette array [i] til 0. Det ser ut som jeg aldri bryte ut av dette for loop. Hvis jeg skriver i, så jeg er 2, så skal jeg gå neste. Jeg skal skrive i, i er 3, så skal jeg gå neste. Jeg skal skrive i og jeg er 3. Deretter skriver jeg, er jeg 4. Egentlig er print sizeof (matrise), så størrelsen på matrisen 20. Men det ser ut som om det er noen spesiell gdb kommando for å gå til noe skjer. Det er som å sette en tilstand på verdien av variabelen. Men jeg husker ikke hva det er. Så hvis vi fortsetter - Hva sa du? Hva gjorde du med opp? [Student] Vises jeg legge - >> Ja. Så viser jeg kan hjelpe. Hvis vi bare vise i, vil det satt opp her hva verdien av jeg er så jeg trenger ikke å skrive den ut hver gang. Hvis vi fortsetter bare neste, ser vi 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5. Noe kommer fryktelig galt, og jeg er tilbakestilt blir til 0. Ser på buggy4.c, ser vi alt som skjer er int array [5]; for (i = 0; i <= sizeof (matrise); i + +) array [i] = 0; Hva ser vi det er galt her? Som et hint, da jeg gjorde gdb buggy4 - la oss bryte viktigste, run - Jeg gjorde print sizeof (matrise) bare for å se hva tilstanden er der jeg skal endelig bryte ut. Hvor er jeg? Sa jeg kjøre? Jeg gjorde ikke erklære ennå. Så ut sizeof (matrise), og det er 20, som er forventet siden min array er av størrelse 5 og det er av 5 heltall, så hele greia bør være 5 * sizeof (int) byte, hvor sizeof (int) har en tendens til å være fire. Så sizeof (array) er 20 år. Hva skal dette være? [Student] Fordelt på sizeof (int). >> Ja, / sizeof (int). Det ser ut som det fortsatt er et problem her. Jeg tror dette bør bare være < siden det er stort sett alltid > [Bowden] Ja. Når vi kommer utover slutten av tabellen vår, liksom at rommet som vi overstyrer overstyrer verdien av jeg. Og så hvis vi ser inn buggy4, bryte viktigste, løpe, la oss skrive inn adressen i. Det ser ut som det er bffff124. La oss nå skrive inn adressen til array [0]. 110. Hva om [1]? 114. [2], 118. 11c, 120. array [5] er bfff124. Så matrise [5] har samme adresse som jeg, som betyr at matrisen [5] er i. Hvis de har samme adresse, er de samme. Så når vi setter array [5] til 0, setter vi i til 0. Og hvis du tenker på dette i form av stabelen, int i er erklært først, noe som betyr at jeg får litt plass på stakken. Så array [5] er tildelt, så da 20 byte fordeles på stakken. Så jeg blir tildelt først, deretter disse 20 bytes får tildelt. Så jeg skjer rett før array, og på grunn av måten som jeg sa i forrige uke, der teknisk stabelen vokser ned, når du indeks til en matrise, er vi garantert at 0th posisjonen i matrisen alltid skjer før den første posisjonen i matrisen. Dette er slags hvordan jeg trakk det forrige uke. Legg merke til at på bunnen har vi adresse 0 og øverst har vi adresse Maks. Stabelen er alltid vokser ned. La oss si at vi fordele jeg. Vi tildeler heltall i, som betyr la oss bare si opp her heltall i blir tildelt. Da vi fordele vår rekke av 5 heltall, noe som betyr at under det, siden stabelen vokser ned, får de 5 heltall tildelt. Men på grunn av hvordan arrayer fungerer, vi garantert at den første posisjonen i matrisen alltid har en adresse mindre enn den andre ting i matrisen. Så matrise posisjon 0 har alltid skje først i minnet, mens matrise posisjon 1 må skje etter at og utvalg posisjon 2 må skje etter det, noe som betyr at matrisen posisjon 0 ville skje et sted her nede, matrise posisjon 1 ville skje over at fordi flytte opp betyr høyere adresser siden det maksimale adresse er her oppe. Så array [0] her nede, array [1] her oppe, array [2] opp her, array [3] opp her. Legg merke til hvordan før vi tildelt heltall i hele veien opp her, når vi flytter lenger og lenger inn i matrisen vår, vi kommer nærmere og nærmere vår heltall jeg. Det bare så skjer at array [5], som er en posisjon utenfor rekke vår, er akkurat der heltall jeg skjedd å bli tildelt. Så det er det punktet hvor vi måtte bli trykket plassen på stakken som ble tildelt for heltall i, og vi setter det til 0. Det er slik det fungerer. Spørsmål? Ja. [Student] Never mind. Okay. [Student] Hvordan kan du unngå disse slags feil? Disse slags feil? Ikke bruk C som programmeringsspråk. Bruke et språk som har matrise grensekontroll. Så lenge du er forsiktig, du trenger bare å unngå å gå forbi grensene av arrayet. [Student] Så her når vi gikk forbi grensene for arrayet - [Bowden] Det er der ting begynner å gå galt. >> [Student] Oh, okay. Så lenge du holder deg innenfor den minnebruken for arrayet, du er fin. Men C gjør ingen feilkontroll. Hvis jeg gjør array [1000], vil det gjerne bare endre hva som skjer - Det går til begynnelsen av tabellen, så det går 1000 stillinger etter og setter den til 0. Det gjør ikke noe sjekke at oh, betyr ikke egentlig har 1000 ting i den. 1000 er langt mer enn hva jeg bør forandres, mens Java eller noe du får rekke utenfor banen indeksen eller indeks utenfor banen unntak. Det er derfor mye høyere nivå språk har disse tingene der hvis du går utenfor grensene av tabellen, mislykkes du slik at du ikke kan endre ting fra under deg og deretter ting går mye verre enn bare å få et unntak si at du gikk utover slutten av tabellen. [Student] Og så bør vi ha bare endret <= å bare > [Bowden] Yeah. Det bør være > [Student] Høyre. Flere spørsmål? Okay. [Student] Jeg har et spørsmål. >> Ja. [Student] Hva er den faktiske tabell variabel? [Bowden] Som hva er array? Array seg selv er et symbol. Det er bare adressen til starten av 20 byte som vi refererer. Du kan tenke på det som en peker, men det er en konstant peker. Så snart ting blir samlet, ikke variabel rekke ikke eksisterer lenger. [Student] Så hvordan finner det størrelsen på array? Størrelse av array refererer til størrelsen av denne blokken som det symbolet refererer til. Når jeg gjør noe sånt printf ("% p \ n", array); la oss kjøre den. Hva gjorde jeg bare gjøre galt? Array 'utvalg' erklærte her. Oh, her oppe. Clang er flink, og det skjer for å legge merke til at jeg erklærte matrisen som 5 elementer men jeg indeksering i posisjon 1000. Det kan gjøre det fordi dette er bare konstanter. Det kan bare gå så langt i å legge merke til at jeg går utover grensene i tabellen. Men legg merke før når vi hadde jeg være feil, det kan umulig bestemme hvor mange verdier jeg kunne ta på, så det ikke kan fastslå at jeg skulle utover slutten av tabellen. Det er bare Clang være flink. Men nå gjør buggy4. Så hva annet jeg gjør galt? Implisitt erklære biblioteket funksjon 'printf'. Jeg kommer til å ønske å # include . Okay. Nå kjører buggy4. Skrive verdien i matrisen som jeg gjorde her, skrive den som en peker utskrifter noe som ser ut som dette - bfb8805c - som er noen adresse som er i bunken-ish regionen. Array selv er som en peker, men det er ikke en faktisk peker, siden en vanlig pekeren kan vi endre. Array er bare noen konstant. De 20 minneblokker starter på adresse 0xbfb8805c. Så bfb8805c gjennom denne adressen +20--eller jeg antar -20 - er hele hukommelsen allokert for denne matrisen. Matrise, blir variabelen selv ikke lagret noe. Når du kompilering, kompilatoren - hånd bølge på det - men kompilatoren vil bare bruke hvor det vet matrise å være. Det vet hvor denne matrisen starter, og slik at det kan alltid bare gjøre ting i form av forskyvninger fra at begynnelsen. Det trenger ikke en variabel i seg selv å representere array. Men når jeg gjør noe sånt int * p = array, nå p er en peker som peker til denne matrisen, og nå p faktisk eksisterer på stakken. Jeg er fri til å endre p. Jeg kan gjøre p = malloc. Så det opprinnelig pekte rekke, nå den peker på noen plass på haugen. Jeg kan ikke gjøre rekke = malloc. Hvis Clang er smart, vil det kjefte på meg rett utenfor balltre. Faktisk er jeg ganske sikker på at gcc ville gjøre dette også. Så rekke type 'int [5]' er ikke overdras. Du kan ikke tildele noe til en rekke typer fordi matrise er bare en konstant. Det er et symbol som refererer de 20 bytes. Jeg kan ikke endre det. [Student] Og hvor er størrelsen på tabellen lagret? [Bowden] Det er ikke lagret noe sted. Det er når det er kompilering. Så der er størrelsen på matrisen lagret? Du kan bare bruke sizeof (matrise) innsiden av den funksjonen som matrisen er erklært seg selv. Så hvis jeg gjør noen funksjon, foo, og jeg (int array []) printf ("% d \ n", sizeof (matrise)); og deretter ned her jeg kaller foo (array); innsiden av denne funksjonen - la oss kjøre den. Dette er Clang være flink igjen. Det forteller meg at sizeof på rekke funksjon parameter vil returnere størrelsen 'int *'. Dette ville være en feil hvis det ikke er det jeg ønsket skulle skje. La oss faktisk slå av Werror. Advarsel. Advarsler er fine. Det vil fortsatt kompilere så lenge den har en advarsel. . / A.out kommer til å skrive ut 4. Advarselen som ble generert er en klar indikator på hva som gikk galt. Dette int array er bare kommer til å skrive ut sizeof (int *). Selv om jeg setter array [5] her, er det fortsatt bare kommer til å skrive ut sizeof (int *). Så så snart du passerer det inn i en funksjon, skillet mellom arrays og pekere er ikke-eksisterende. Dette skjer for å være en matrise som ble erklært på stakken, men så snart vi passerer den verdien som 0xBF blah, blah, blah inn i denne funksjonen, så denne pekeren peker på at matrisen på stakken. Så det betyr at sizeof gjelder bare i funksjonen at matrisen ble erklært, noe som betyr at når du kompilere denne funksjonen, når Clang går gjennom denne funksjonen, ser det matrise er en int array av størrelse 5. Så da det ser sizeof (array). Vel, det er 20. Det er faktisk hvordan sizeof utgangspunktet fungerer for nesten alle tilfeller. Sizeof er ikke en funksjon, det er en operatør. Du trenger ikke kalle sizeof funksjonen. Sizeof (int), vil kompilatoren bare oversette det til 4. Fikk den? Okay. [Student] Så hva er forskjellen mellom sizeof (matrise) i hoved-og i Foo? Dette er fordi vi sier sizeof (matrise), som er av typen int *, mens tabellen her nede er ikke av typen int *, er det en int array. [Student] Så hvis du hadde parameter i array [] i stedet for int * array, ville det bety at du kan fortsatt endre utvalg fordi nå er det en peker? [Bowden] Liker du dette? >> [Student] Yeah. Kan du endre utvalg i funksjonen nå? [Bowden] Du kan endre array i begge tilfeller. I begge disse tilfellene er du fri til å si array [4] = 0. [Student] Men kan du gjøre utvalg peker på noe annet? [Bowden] Oh. Ja. I begge tilfeller - >> [student] Yeah. [Bowden] Skillet mellom array [] og en int * array, det er ingen. Du kan også få noen flerdimensjonal array her for noen praktisk syntaks, men det er fortsatt bare en peker. Dette betyr at jeg er fri til å gjøre utvalg = malloc (sizeof (int)), og nå peker et annet sted. Men akkurat som hvordan dette fungerer for evig og alltid, endre denne tabellen ved å gjøre det peke på noe annet endrer ikke denne tabellen her nede fordi det er en kopi av argumentet, det er ikke en peker til dette argumentet. Og faktisk, akkurat som mer indikasjon på at det er akkurat det samme - vi allerede så hva utskrift rekke utskrifter - hva om vi skriver inn adressen til matrisen eller adressen til adressen i matrisen til en av dem? La oss ignorere dette. Okay. Dette er greit. Det er nå i gang. / A.out. Utskrift array, deretter skrive inn adressen til array, er det samme. Array bare ikke eksisterer. Den vet når du skal skrive ut matrisen, du skriver symbolet som refererer til de 20 byte. Skrive ut adressen til array, vel, ikke matrise ikke eksisterer. Det har ikke en adresse, slik at det bare skriver adresse til de 20 byte. Så snart du kompilere ned, som i en samlet buggy4. / A.out, matrise er ikke tilstede. Pekere eksisterer. Arrays ikke. Blokkene minne representerer tabellen fortsatt eksisterer, men den variable matrise og variabler av denne typen ikke eksisterer. De er som de viktigste forskjellene mellom arrays og pekere er så snart du gjør funksjonskall, er det ingen forskjell. Men innsiden av funksjonen som matrisen selv er erklært, fungerer sizeof annerledes siden du skriver størrelsen av blokkene i stedet for størrelsen av den type, og du kan ikke endre det fordi det er et symbol. Skrive ut ting og adressen til ting skriver det samme. Og det er ganske mye det. [Student] Kan du si det en gang til? Jeg kan ha gått glipp av noe. Utskrift rekke og adresse rekke skriver det samme, mens hvis du skriver ut en peker mot adressen pekeren, den ene tingen skriver adressen på hva du peker til, den andre skriver adressen pekeren på stakken. Du kan endre en peker, du kan ikke endre en rekke symbol. Og sizeof pekeren kommer til å skrive ut størrelsen på at pekeren type. Så int * p sizeof (p) kommer til å skrive ut 4, men int array [5] print sizeof (matrise) kommer til å skrive ut 20. [Student] Så int array [5] vil skrive 20? >> Ja. Det er derfor inne i buggy4 når det pleide å være sizeof (array) dette gjorde jeg <20, som er ikke hva vi ønsket. Vi ønsker i <5. >> [Student] Okay. [Bowden] Og så så snart du begynner å passere i funksjonene, hvis vi gjorde int * p = array; innsiden av denne funksjonen, kan vi i utgangspunktet bruke p og utvalg i nøyaktig de samme måter, unntak sizeof problemet og skiftende problem. Men p [0] = 1; er det samme som å si matrise [0] = 1; Og så snart vi si foo (array), eller foo (p); innsiden av foo funksjon, er dette det samme anropet to ganger. Det er ingen forskjell mellom disse to samtalene. Alle gode på det? Okay. Vi har 10 minutter. Vi skal prøve å komme gjennom denne Hacker Typer program, dette nettstedet, som kom ut i fjor eller noe. Det er bare ment å være som du skriver tilfeldig og det skrives ut - Uansett fil det skjer å ha lastet er hva det ser ut som du skriver. Det ser ut som en slags operativsystem kode. Det er hva vi ønsker å gjennomføre. Du bør ha en binær kjørbar heter hacker_typer som tar i et enkelt argument, filen til "hacker type." Kjører den kjørbare bør tømme skjermen og deretter skrive ut en karakter fra bestått-in fil hver gang brukeren trykker på en knapp. Så uansett hva tast du trykker på, bør det kaste bort og i stedet skrive ut et tegn fra filen som er argumentet. Jeg får ganske mye fortelle deg hva de tingene vi kommer til å trenge å vite er. Men vi ønsker å sjekke ut termios biblioteket. Jeg har aldri brukt dette biblioteket i hele mitt liv, så det har svært minimale formål. Men dette kommer til å være på biblioteket vi kan bruke til å kaste bort tegnet du treffer når du skriver inn standard i. Så hacker_typer.c, og vi kommer til å ønske å # include . Ser på mannen siden for termios - jeg gjetter det er terminal OS eller noe - Jeg vet ikke hvordan du skal lese den. Ser på dette, sier det å inkludere disse to filene, så vi får gjøre det. Første ting først, ønsker vi å ta i et enkelt argument, som er filen vi skal åpne. Så hva ønsker jeg å gjøre? Hvordan sjekker jeg å se jeg har et enkelt argument? [Student] Hvis argc lik den. >> [Bowden] Yeah. Så hvis (argc = 2!) Printf ("bruk:% s [filen for å åpne]"). Så nå hvis jeg kjører denne uten å gi et annet argument - oh, jeg trenger den nye linjen - vil du se det står bruk:. / hacker_typer, og deretter det andre argumentet bør være filen jeg ønsker å åpne. Nå hva gjør jeg? Jeg ønsker å lese fra denne filen. Hvordan leser jeg fra en fil? [Student] Du åpner den først. >> Ja. Så fopen. Hvordan ser fopen ut? [Student] Filnavn. >> [Bowden] Filnavn kommer til å være argv [1]. [Student] Og hva du vil gjøre med det, så - >> [Bowden] Yeah. Så hvis du ikke husker, kan du bare gjøre mennesket fopen, hvor det kommer til å bli en const char * banen der bane er filnavn, const char * modus. Hvis du tilfeldigvis ikke huske hvilken modus er, så kan du se etter modus. Innsiden av man-sidene, er skråstreken tegnet hva du kan bruke til å søke etter ting. Så jeg skriver / modus for å søke etter modus. n og N er hva du kan bruke til å bla gjennom søk kamper. Her står det argumentet modus peker til en streng begynner med en av de følgende sekvenser. Så r, Open tekstfil for lesing. Det er hva vi ønsker å gjøre. For å lese, og jeg ønsker å lagre det. Saken kommer til å bli en FIL *. Nå hva gjør jeg ønsker å gjøre? Gi meg et sekund. Okay. Nå hva gjør jeg ønsker å gjøre? [Student] Sjekk om det er NULL. >> [Bowden] Yeah. Hver gang du åpner en fil, sørg for at du er vellykket i stand til å åpne den. Nå ønsker jeg å gjøre det termios ting der jeg ønsker å først lese min nåværende innstillinger og lagre dem til noe, så jeg ønsker å endre innstillingene mine å kaste bort alle tegn som jeg skriver, og da vil jeg oppdatere disse innstillingene. Og deretter på slutten av programmet, vil jeg bytte tilbake til mitt opprinnelige innstillinger. Så struct kommer til å være av type termios, og jeg kommer til å ha to av disse. Den første kommer til å være mine current_settings, og da de kommer til å være mine hacker_settings. Først, jeg kommer til å ønske å lagre mine gjeldende innstillinger, så jeg kommer til å ønske å oppdatere hacker_settings, og deretter helt på slutten av mitt program, vil jeg gå tilbake til gjeldende innstillinger. Så sparer gjeldende innstillinger, slik som fungerer, vi mann termios. Vi ser at vi har denne int tcsetattr, int tcgetattr. Jeg passerer i en termios struct av pekeren sin. Måten dette skal se ut er - jeg har allerede glemt hva funksjonen ble kalt. Kopier og lim inn. Så tcgetattr, så jeg ønsker å passere i struct at jeg lagrer informasjonen i, som kommer til å bli current_settings, og det første argumentet er filen beskrivelse for ting jeg ønsker å lagre egenskapene til. Hva filen descriptor er er som når du åpner en fil, blir det en fil descriptor. Når jeg fopen argv [1], blir det en fil descriptor som du refererer når du ønsker å lese eller skrive til den. Det er ikke filen descriptor jeg ønsker å bruke her. Det er tre fildeskriptorer du har som standard, som er standard i, standard ut, og standard feil. Som standard, tror jeg det er standard i er 0, er standard ut 1, og standard feil er to. Så hva ønsker jeg å endre innstillingene for? Jeg ønsker å endre innstillingene for hver gang jeg traff en karakter, Jeg vil at det skal kaste det tegnet bort i stedet for å skrive den til skjermen. Hva stream - standard inn, standard ut, eller standard feil - reagerer på ting når jeg skriver på tastaturet? >> [Student] Standard i. >> Ja. Så jeg kan enten gjøre 0 eller jeg kan gjøre stdin. Jeg får current_settings av standard i. Nå ønsker jeg å oppdatere disse innstillingene, så først skal jeg kopiere inn hacker_settings hva mine current_settings er. Og hvordan structs arbeid er det bare kopiere. Dette kopierer alle feltene, som du forventer. Nå ønsker jeg å oppdatere noen av feltene. Ser på termios, ville du nødt til å lese gjennom mye av dette bare for å se hva du ønsker å se etter, men flaggene du kommer til å ønske å se etter er ekko, så ECHO ECHO inn tegn. Først vil jeg sette - jeg har allerede glemt hva feltene er. Dette er hva struct ser ut. Så inntastingsmoduser jeg tror vi ønsker å endre. Vi skal se på løsningen for å sikre det er det vi ønsker å endre. Vi ønsker å endre lflag for å hindre ønsker å se gjennom alle disse. Vi ønsker å endre lokale moduser. Du vil måtte lese gjennom hele denne tingen å forstå hvor alt tilhører at vi ønsker å endre. Men det er på innsiden av lokale moduser hvor vi kommer til å ønske å endre det. Så hacker_settings.cc_lmode er hva det heter. c_lflag. Det er her vi kommer inn bitvis operatører. Vi er slags ut av tiden, men vi vil gå gjennom det virkelig rask. Det er her vi kommer inn bitvis operatører, hvor jeg tror jeg sa en gang for lenge siden at når du begynner å gjøre med flagg, du kommer til å bruke bitvis operatør mye. Hver bit i flagget tilsvarer en slags oppførsel. Så her har dette flagget en haug med forskjellige ting, hvor alle av dem mener noe annet. Men det jeg ønsker å gjøre er å slå like ved litt som tilsvarer ECHO. Så for å slå det av jeg gjør og = ¬ ECHO. Egentlig tror jeg det er som Techo eller noe. Jeg skal bare sjekke igjen. Jeg kan termios det. Det er ECHO bare. ECHO skal være en enkelt bit. ¬ ECHO kommer til å bety alle biter er satt til 1, som betyr at alle flagg er satt til sann unntatt for ECHO bit. Ved å avslutte min lokale flagg med dette, betyr det alle flagg som for øyeblikket satt til sann vil fortsatt bli satt til true. Hvis min ECHO flagget er satt til true, så dette er nødvendigvis satt til false på ECHO flagg. Så denne linjen med kode bare slår av ECHO flagg. De andre linjer med kode, vil jeg bare kopiere dem av hensyn til tid og deretter forklare dem. I løsningen, sa han 0. Det er trolig bedre å eksplisitt si stdin. Legg merke til at jeg også gjør ECHO | icanon her. Icanon refererer til noe separat, noe som betyr kanoniske modus. Hva kanoniske modus betyr er vanligvis når du skriver ut kommandolinjen, standard i ikke behandler noe før du treffer linjeskift. Så når du GetString, skriver du en haug av ting, så du treffer linjeskift. Det er da det er sendt til standard i. Det er standard. Når jeg slår av kanonisk modus, nå hver enkelt tegn du trykker er hva som blir behandlet, som vanligvis er slags dårlig fordi den er treg til å behandle disse tingene, det er derfor det er godt å bufre det inn hele linjer. Men jeg vil hvert tegn som skal behandles siden jeg ikke vil at den skal vente på meg til å treffe linjeskift før den behandler alle tegnene jeg har vært å skrive. Dette slår av kanoniske modus. Dette ting betyr bare når det faktisk behandler tegn. Dette betyr behandle dem umiddelbart, så snart jeg skriver dem, behandle dem. Og dette er den funksjonen som oppdaterer mine innstillinger for standard i, og TCSA del gjøre det akkurat nå. De andre alternativene er vente til alt som er på strømmen er behandlet. Det spiller ingen rolle. Akkurat nå endre innstillingene mine for å være det som er for tiden i hacker_typer_settings. Jeg antar jeg kalte det hacker_settings, så la oss endre det. Endre alt til hacker_settings. Nå på slutten av vårt program vi kommer til å ønske å gå tilbake til det som nå er inne i normal_settings, som kommer til å bare se ut og normal_settings. Merker jeg har ikke endret noen av mine normal_settings siden opprinnelig å få det. Deretter å bare endre dem tilbake, passerer jeg dem tilbake på slutten. Dette var oppdateringen. Okay. Nå inne for her vil jeg bare forklare koden i interesse av tid. Det er ikke så mye kode. Vi ser vi leser et tegn fra filen. Vi kalte det f. Nå kan du mann fgetc, men hvordan fgetc skal fungere er bare det kommer til å returnere tegnet du bare lese eller EOF, som svarer til slutten av filen eller noen feil skjer. Vi er looping, fortsetter å lese en enkelt tegn fra filen, før vi har kjørt ut av tegn å lese. Og mens vi gjør det, vi vente på et enkelt tegn fra standard i. Hver eneste gang du skriver noe på kommandolinjen, som er å lese i en karakter fra standard i. Da putchar er bare kommer til å sette char vi leser opp her fra filen til standard ut. Du kan mannen putchar, men det er bare å sette til standard ut, det skriver det tegnet. Du kan også bare gjøre printf ("% c", c); Samme idé. Det kommer til å gjøre mesteparten av arbeidet vårt. Det siste vi kommer til å ønske å gjøre er bare fclose vår fil. Hvis du ikke fclose, det er en minnelekkasje. Vi ønsker å fclose filen vi opprinnelig åpnet, og jeg tror det er det. Hvis vi gjør det, jeg har allerede fått problemer. La oss se. Hva gjorde det klage? Forventet 'int', men argumentet er av typen 'struct _IO_FILE *'. Vi får se om det fungerer. Bare tillatt i C99. Augh. Ok, gjør hacker_typer. Nå får vi mer nyttige beskrivelser. Så bruk av svart identifikator 'normal_settings'. Jeg gjorde ikke kalle det normal_settings. Jeg kalte det current_settings. Så la oss endre alt dette. Nå passerer argument. Jeg skal gjøre dette 0 for nå. Okay. . / Hacker_typer cp.c. Jeg gjorde ikke også tømme skjermen i begynnelsen. Men du kan se tilbake til den siste oppgavesettet for å se hvordan du fjerner skjermen. Det er bare å skrive ut noen tegn mens dette gjør hva jeg vil gjøre. Okay. Og tenker på hvorfor dette måtte være 0 i stedet for stdin, som bør # define 0, Dette er klager på at - Før når jeg sa at det er fildeskriptorer men så har du også din FIL *, en fil descriptor er bare en enkelt heltall, mens en fil * har en hel haug med ting knyttet til den. Grunnen til at vi trenger å si 0 i stedet for stdin er at stdin er en fil * som peker til det som er refererer fil descriptor 0. Så selv opp her når jeg gjør fopen (argv [1], jeg får en fil * tilbake. Men et sted i denne filen * er en ting som tilsvarer filen descriptor for denne filen. Hvis du ser på mannen siden for å åpne, så jeg tror du må gjøre mann 3 open - nope - man 2 open - yeah. Hvis du ser på siden for å åpne, er åpen som en lavere nivå fopen, og den returnerer selve filen descriptor. fopen gjør en haug med ting på toppen av åpne, som i stedet for å returnere bare den filen descriptor returnerer en hel fil * pekeren innsiden av som er vår lille fil descriptor. Så standard i refererer til FIL * ting, mens 0 refererer til bare filen descriptor standard i seg selv. Spørsmål? [Ler] blåste gjennom det. OK. Vi er ferdige. [Ler] [CS50.TV]