[Powered by Google Translate] [Avsnitt 5 - Mer Bekväm]
[Rob Bowden - Harvarduniversitetet]
[Detta är CS50. - CS50.TV]
>> Som jag sade i min e-post, det finns en massa saker som du kan använda
annat än apparaten att faktiskt göra problemet uppsättningar.
Vi rekommenderar att du gör det i apparaten bara för att vi lättare kan hjälpa dig
och vi vet hur allt kommer att fungera.
Men som ett exempel på där man kan göra saker om, säg, har du inte tillgång till
till en apparat eller om du vill arbeta i Science Center källaren -
som faktiskt de har apparaten också -
Om du vill arbeta var som helst.
Ett exempel är har du sett / hört talas om SSH?
SSH är i princip precis som ansluter till någonting.
Egentligen, just nu är jag SSHed i apparaten.
Jag arbetar aldrig direkt i apparaten.
Här är apparaten, och om du tittar här nere ser du denna IP-adress.
Jag arbetar aldrig i själva apparaten;
Jag kommer alltid fram till en iTerm2 fönster / terminalfönster.
Du kan SSH för att den IP-adressen, ssh jharvard@192.168.129.128.
Jag minns att antalet mycket lätt eftersom det är en sådan trevlig mönster.
Men det kommer att fråga mig för mitt lösenord, och nu är jag i apparaten.
I grund och botten, på denna punkt, om du öppnat en terminal inuti själva apparaten,
detta gränssnitt, men du skulle använda den, är exakt samma
som gränssnitt jag använder hit men nu är du SSHed.
Du behöver inte SSH till apparaten.
Ett exempel på en annan plats kan du SSH till är jag är ganska säker på att du har som standard -
Åh. Större.
Alla ni bör ha med konton standard FAS på FAS servrar.
För mig skulle jag SSH till rbowden@nice.fas.harvard.edu.
Det kommer att fråga dig om att första gången, och du säger ja.
Mitt lösenord är bara att vara min FAS lösenord.
Och så nu, jag SSHed till fina servrar och jag kan göra vad jag vill här.
Många klasser du kan ta, liksom 124, kommer att få dig upp saker hit
att faktiskt skicka dina problem uppsättningar.
Men säger att du inte har tillgång till din apparat.
Sedan kan du göra saker, som på här det kommer att säga -
Detta är bara vår del av frågorna.
Den kommer att fråga dig att göra detta i apparaten.
Istället ska jag bara göra det på servern.
Jag ska packa det.
Problemet kommer att vara att du är van att använda något som gedit
eller vad inuti apparaten.
Du kommer inte att få det på FAS-servern.
Allt är bara kommer att vara så här text gränssnitt.
Så du kan antingen en, försöka lära en textredigerare som de har.
De har Nano.
Nano är oftast ganska lätt att använda.
Du kan använda dina pilar och skriva normalt.
Så det är inte svårt.
Om du vill bli riktigt fint du kan använda Emacs,
som jag förmodligen inte skulle ha öppnat för jag vet inte ens hur man stänger Emacs.
Kontroll X Kontroll C? Ja.
Eller så kan du använda Vim, vilket är vad jag använder.
Och så de är dina alternativ.
Om du inte vill göra det, kan du också, om man tittar på manual.cs50.net--
Åh.
På en PC kan du SSH använder PuTTY,
som du kommer att behöva ladda ner separat.
På en Mac kan du bara som standard använder Terminal eller så kan du ladda ner iTerm2,
som är som en trevlig, fin terminal.
Om du går till manual.cs50.net ser du en länk till Notepad + +,
vilket är vad du kan använda på en PC.
Det låter dig SFTP från Notepad + +, vilket är i stort sett SSH.
Vad detta kommer att låta dig göra är redigera dina filer lokalt,
och sedan när du vill spara dem, kommer det att spara till nice.fas,
där du sedan kan köra dem.
Och motsvarande på en Mac kommer att bli TextWrangler.
Så det kan du göra samma sak.
Det låter dig redigera filer lokalt och spara dem till nice.fas,
där du sedan kan köra dem.
Så om du någonsin fastnat utan en apparat har du dessa alternativ
att fortfarande göra dina problemområden uppsättningar.
Ett problem kommer att vara att du inte kommer att ha CS50 biblioteket
eftersom nice.fas inte har som standard att.
Du kan antingen ladda ner CS50 biblioteket -
Jag tror inte jag behöver det på denna punkt.
Du kan antingen ladda ner CS50 biblioteket och kopiera den till nice.fas,
eller jag tror på denna punkt vi inte använder det längre ändå.
Eller om vi gör, kan du tills vidare ersätta det med
Verkställandet av funktionerna i CS50 biblioteket ändå.
Så det bör inte vara så mycket av en begränsning.
Och det är det.
>> Jag ska gå tillbaka till apparaten nu, vi kommer att göra allt i apparaten.
Titta på vår del av frågorna, i början, som jag sade i min e-post,
Vi måste prata om en kort man skulle titta på.
Vi har omdirigera & Rör och dessa tre frågor.
>> Till vilken ström skriver funktioner som printf som standard?
Så ström. Vad är en ström?
En ström är i princip som det är bara några -
Det är inte ens en källa till 1: or och 0: or.
Strömmen det ber om här är vanlig ute.
Och så vanlig ut är en ström som när man skriver det,
det visas på skärmen.
Standard ut genom ström, betyder det att du bara skriva 1s och 0s till det,
och den andra änden av standard ut läser bara från denna ström.
Det är bara en sträng av 1: or och 0: or.
Du kan skriva till strömmar eller du kan läsa från strömmar
beroende på vad strömmen faktiskt är.
De andra två förinställda flöden är standard i och standardfel.
Standard i är när du gör getString, det väntar på dig att mata grejer.
Så det väntar på dig, det väntar faktiskt på standard i,
som verkligen är vad du får när du skriver på tangentbordet.
Du skriver till standard i.
Standardfel är i grunden likvärdig med standard ut,
men det är specialiserade på att när du skriver ut på standard fel,
du ska bara skriva ut felmeddelanden som
så du kan skilja mellan vanliga meddelanden utskrivna på skärmen
kontra felmeddelanden beroende på om de gick till standard ut eller standardfel.
Filer också.
Standard ut, standard i och standardfelet är bara speciella strömmar,
men egentligen alla filer när du öppnar en fil, blir det en ström av bytes
där du kan bara läsa från denna ström.
Du, för det mesta, kan bara tänka på en fil som en ström av bytes.
Så vad strömmar skriver de att som standard? Standard ut.
>> Vad är skillnaden mellan> och >>?
Har någon titta på videon i förväg? Okej.
> Kommer att bli hur du omdirigerar till filer,
och >> kommer också att dirigera utdata i filer,
men det är i stället kommer att lägga till filen.
Till exempel, låt oss säga att jag råkar ha dict här,
och den enda grejer inuti dict är katt, katt, hund, fisk, hund.
Ett kommando som du har på kommandoraden är katt,
som just kommer att skriva ut vad som finns i en fil.
Så när jag säger katt dict, det kommer att skriva ut katt, katt, hund, fisk, hund. Det är allt katt gör.
Det betyder att det skrivs till standard ut katt, katt, hund, fisk, hund.
Om jag istället vill omdirigera det till en fil, kan jag använda> och omdirigera den till vad filen.
Jag ringer filen filen.
Så nu om jag ls, jag ser har jag en ny fil som heter filen.
Och om jag öppnar det, det kommer att ha exakt vad katten sätta på kommandoraden.
Så nu om jag gör det igen, då det kommer att omdirigera utdata till filen,
och jag kommer att ha exakt samma sak.
Så tekniskt, förbigick det helt vad vi hade.
Och vi får se om jag ändrar dict tog jag ut hunden.
Nu om vi katt dict i filen igen, vi kommer att ha den nya versionen med hund bort.
Så det åsidosätter helt det.
Istället, om vi använder >>, det kommer att lägga filen.
Nu öppnar filen ser vi att vi har precis samma sak skrivs två gånger
eftersom det var det en gång, då vi fogas till den ursprungliga.
Så det är vad> och >> gör.
Frågar nästa - Det spelar ingen fråga om det.
>> Den andra som vi har är <, som om> omdirigerar standard ut,
<Kommer att omdirigera standard i.
Låt oss se om vi har ett exempel.
Jag kan skriva en riktigt snabb.
Låt oss ta en fil, hej.c.
Relativt enkel fil.
Jag bara få en sträng och sedan skriva "Hej" oavsett strängen jag angav var.
Så gör hej och då. / Hej.
Nu är det föranledde mig att skriva något,
vilket betyder att det väntar på saker som ska införas i standard i.
Så skriv vad jag vill i standard i. Vi ska bara säga hej, Rob!
Då är det du skriver till standard ut Hej, Rob!
Om jag gör. / Hej och sedan omdirigera,
för nu kan du bara styra från en fil.
Så om jag sätter i någon fil, txt, och jag satte Rob,
om jag kör hej och sedan dirigera filen txt till. / hej, det kommer att säga Hej, Rob! omedelbart.
När det blir först GetString och väntar på standard i,
standard är inte längre vänta på tangentbordet för data att få angett.
I stället har vi omdirigerat standard i att läsa från filen txt.
Och så det kommer att läsa från filen txt, vilket är just den linjen Rob,
och då det kommer att skriva ut Hej, Rob!
Och om jag ville kunde jag också göra. / Hej <txt
och sedan den standard att det tryckeri som är Hej, Rob!,
Jag kan omdirigera det till en egen fil.
Jag ska bara ringa filen hej - nej, jag kommer inte, eftersom det är den körbara - txt2.
Nu är txt2 kommer att ha effekt på. / Hej <txt, vilket kommer att bli Hej, Rob!
>> Frågor?
>> Okej. Så så här har vi rörledning.
Rör är den sista enheten av omdirigering.
>> Åh. Jag antar en enhet av omdirigering är om istället för> du gör 2>,
som är omdirigera standard fel.
Så om något gick till standard fel, skulle det inte få sätta in txt2.
Men märker om jag gör 2>, då är det fortfarande skriva Hej, Rob! till kommandoraden
eftersom jag bara omdirigera standard fel, jag styra inte standard ut.
Standardfel och standard ut är olika.
Om du ville verkligen skriva till standard fel,
då jag kunde ändra detta vara fprintf till stderr.
Så printf som standard skriver till standard ut.
Om jag vill skriva ut till standard fel manuellt, då måste jag använda fprintf
och ange vad jag vill skriva ut på.
Om man i stället gjorde jag fprintf stdout, så det är i princip likvärdiga med printf.
Men fprintf till standard fel.
Så nu, om jag omdirigera detta till txt2, Hej, Rob! fortfarande få ut på kommandoraden
eftersom det blir tryckt till standard fel och jag bara omdirigera standard ut.
Om jag nu omdirigera standard fel, nu är det inte få ut och txt2 kommer att bli Hej, Rob!
Så nu kan du skriva ut dina faktiska fel till standard fel
och skriva ut dina vanliga meddelanden till standard ut.
Och så när du kör programmet kan du köra det som. / Hej denna typ med 2>
så att ditt program ska fungera normalt,
men alla felmeddelanden som du får du kan kontrollera senare i felloggen
så fel, och sedan titta senare och dina fel fil kommer att ha några fel som hände.
>> Frågor?
>> Den sista är rör, som du kan tänka på som att ta standarden ur ett kommando
och gör den till den standard på nästa kommando.
Ett exempel här är eko är en kommandorad sak
som bara kommer att upprepa vad jag satte som argument. Jag kommer inte att sätta citattecken.
Echo bla, bla, är bla ska bara skriva ut bla, bla, bla.
Tidigare, när jag sa att jag var tvungen att sätta Rob i en txt-fil
eftersom jag bara kan omdirigera txt-filer i stället / om jag upprepar Rob
och sedan rör den till. / hej, som kommer att också göra samma typ av sak.
Detta tar produktionen av detta kommando, eko Rob,
och använda den som underlag för. / hej.
Du kan se det som första omdirigeringen eko Rob i en fil
och mata in. / hej filen som just matas ut.
Men det tar den temporära filen ur bilden.
>> Frågor på det?
>> Nästa fråga kommer att innebära detta.
Vilken rörledning kan du använda för att hitta antalet unika namn i en fil som heter names.txt?
Kommandona vi kommer att vilja använda här är unika, så uniq och sedan wc.
Du kan göra människan Uniq att faktiskt titta på vad det gör,
och det är bara att filtrera intilliggande matchande rader från ingången.
Och människan wc kommer att skriva ut nyrad, ord och byte räknas för varje fil.
Och den sista vi kommer att vilja använda är sortera,
som kommer att bara sortera raderna i txt-fil.
Om jag gör några txt-fil, names.txt, och det är Rob, Tommy, Josef, Tommy, Josef, RJ, Rob,
vad jag vill göra här är att hitta antalet unika namn i denna fil.
Så vad ska svaret vara? >> [Elev] 4. >> Ja.
Det bör vara 4 eftersom Rob, Tommy, Joseph, RJ är den enda unika namn i denna fil.
Det första steget, om jag bara gör ord räkna names.txt,
Detta är faktiskt säger allt.
Detta är faktiskt utskrift - Nu ska vi se, man wc - radbrytningar, ord och byte count.
Om jag bara bryr sig om linjer, då kan jag bara göra wc-l names.txt.
Så det är steg 1.
Men jag vill inte wc-l names.txt eftersom names.txt bara innehåller alla namn,
och jag vill filtrera bort eventuella icke-unika sådana.
Så om jag gör uniq names.txt, som inte riktigt ge mig vad jag vill
eftersom de duplicerade namnen finns kvar.
Varför det? Varför Uniq gör inte vad jag vill?
[Elev] Den dubbletter inte [ohörbart] >> Ja.
Kom ihåg mansidan för uniq säger Filtrera intilliggande matchande rader.
De är inte angränsande, så det kommer inte filtrera dem.
Om jag sorterar dem först, sortera names.txt kommer att lägga alla dubbla linjer tillsammans.
Så nu Sortera names.txt är det.
Jag kommer att vilja använda det som ingången till uniq, som är | uniq.
Det ger mig Josef, RJ, Rob, Tommy,
och jag vill använda det som ingången till wc-l,
som kommer att ge mig 4.
Som det står här, vad rörledning du använda?
Du kan göra en massa saker som att använda en serie kommandon
där du använder utdata från ett kommando som ingång till nästa kommando.
Du kan göra en massa saker, en massa smarta saker.
>> Frågor?
Okej.
Det var allt för rör och omdirigering.
>> Nu ska vi gå vidare till själva grejer, kodning grejer.
Inuti denna PDF, ser du det här kommandot,
och du vill köra detta kommando i din apparat.
wget är kommandot för bara få något från Internet, i princip,
så wget och denna URL.
Om du gick till denna URL i din webbläsare, skulle det hämtar filen.
Jag klickade bara på det, så det hämtade filen för mig.
Men skriver wget av den saken inne på terminalen
är bara att ladda ner det till din terminal.
Jag har section5.zip, och du kommer att vilja packa section5.zip,
som kommer att ge dig en mapp som heter avsnitt 5,
som kommer att ha alla de filer vi kommer att använda i dag i den.
Eftersom dessa program "filnamn antyder, de är lite buggig,
så ditt uppdrag är att räkna ut varför använder gdb.
Har alla har dem hämtat / vet hur man får dem ner
i sin apparat? Okej.
>> Köra ./buggy1, kommer det att säga segmenteringsfel (core dumpad),
som varje gång du får en segmenteringsfel, det är en dålig sak.
Under vilka omständigheter får du en segmenteringsfel?
[Elev] Dereferencing en null-pekare. >> Ja. Så det är ett exempel.
Dereferencing en nollpekare du kommer att få en segmenteringsfel.
Vad en segmenteringsfel innebär är att du rör minnet inte bör röra.
Så dereferencing en nollpekare vidrör adress 0,
och i princip alla datorer säger numera att adress 0 är minne du ska inte röra.
Så det är därför dereferencing en nollpekare resulterar i en segmenteringsfel.
När du råkar inte initiera en pekare, så det har en skräp värde,
och så när du försöker dereference det, med all sannolikhet du röra minne
Det är i mitten av ingenstans.
Om du råkar ha tur och skräp värde
råkade peka någonstans på stacken eller något,
sedan när du dereference som pekare som du inte har initierats,
ingenting gå fel.
Men om det är att peka på, säg, någonstans mellan stapeln och högen,
eller det pekar bara någonstans som inte har använts av ditt program ännu,
då du vidrör minne du ska inte röra och du segmenteringsfel.
När du skriver en rekursiv funktion och det recurses för många gånger
och din stack blir för stor och kolliderar stack in i saker
att det inte borde kollidera med, du röra minne du inte bör röra,
så du segmenteringsfel.
Det är vad en segmenteringsfel är.
>> Det är också av samma anledning som om du har en sträng som -
låt oss gå tillbaka till det tidigare programmet.
I hej.c--Jag ska bara göra något annat.
char * s = "Hej världen!";
Om jag använder * s = något eller s [0] = 'X';
så gör hej,. / hej, varför det segmenteringsfel?
Varför har detta segmenteringsfel?
Vad skulle du förvänta dig att hända?
Om jag gjorde det printf ("% s \ n", s), vad skulle du förvänta dig att skrivas?
[Eleven] X hej. >> Ja.
Problemet är att när du deklarerar en sträng som denna,
s är en pekare som kommer att gå på stacken,
och vad s pekar på är denna sträng som ingår i läsminne.
Så bara genom namnet, läsminne, bör du få idén
att om du försöker ändra vad som finns i ROM-minnet,
du gör något du inte bör göra med minne och du segmenteringsfel.
Detta är faktiskt en stor skillnad mellan char * s och röding s [].
Så röding s [], nu denna sträng ska läggas på stacken,
och stapeln är inte skrivskyddad, vilket innebär att denna bör fungera alldeles utmärkt.
Och det gör det.
Kom ihåg att när jag gör char * s = "Hello world!", S själv på stacken
men s pekar någon annanstans, och att någon annanstans råkar vara skrivskyddad.
Men röding s [] är bara något på stacken.
Så det är ett annat exempel på en segmenteringsfel händer.
>> Vi såg att ./buggy1 resulterade i en segmenteringsfel.
I teorin bör du titta inte på buggy1.c omedelbart.
Istället kommer vi att titta på det genom gdb.
Observera att när du får segmenteringsfel (core dumpad),
du får den här filen hit kallas kärna.
Om vi ​​ls-l, vi ser att kärnan är oftast en ganska stor fil.
Detta är antalet byte i filen, så det ser ut som det är 250-någonting kilobyte.
Anledningen till detta är att det kärnan dumpa faktiskt är
är när ditt program kraschar, tillstånd minne av ditt program
bara blir kopieras och klistras in i denna fil.
Det blir dumpad i den filen.
Detta program, medan det körs, råkade ha en minnesanvändning på cirka 250 kilobyte,
och så det är vad blev dumpad i denna fil.
Nu kan du titta på den filen om vi gdb buggy1 kärna.
Vi kan bara göra gdb buggy1, och det kommer bara starta gdb regelbundet,
med buggy1 som sin indatafil.
Men om du gör gdb buggy1 kärna, då är det särskilt kommer att starta gdb
genom att titta på den centrala filen.
Och du säger buggy1 betyder gdb vet att det kärnfil kommer från buggy1 programmet.
Så gdb buggy1 kärna kommer att omedelbart ge oss
där programmet råkade avsluta.
Vi ser här programmet avslutas med signalen 11, segmenteringsfel.
Vi råkar se en rad församling, som troligen är inte till stor hjälp.
Men om du skriver BT eller bakåtspårning, det är kommer att bli funktionen
som ger oss en lista över våra nuvarande stack ramar.
Så bakåtspårning. Det ser ut som vi bara har två stack ramar.
Den första är vår viktigaste stack ram,
och den andra är stacken ramen för denna funktion som vi råkar vara i,
som ser ut som vi bara har assemblerkod för.
Så låt oss gå tillbaka till vår huvudsakliga funktion,
och att göra det vi kan göra ramen 1, och jag tror att vi också kan göra ner,
men jag nästan aldrig göra ner - eller upp. Ja.
Upp och ner. Upp ger dig upp en stack ram, ner tar dig ner en stack ram.
Jag brukar aldrig använda det.
Jag specifikt säga ram 1, vilket är att gå till bildrutan 1.
Ram 1 kommer att föra oss in i stora stack ram,
och det står här kodraden vi råkar vara på.
Om vi ​​ville ett par fler rader kod, kan vi säga listan,
och det kommer att ge oss alla kodrader runt den.
Den linje vi segfaulted på var 6:
if (strcmp ("CS50 rocks", argv [1]) == 0).
Om det inte är uppenbart ännu kan du få det direkt härifrån bara genom att tänka varför det segfaulted.
Men vi kan ta det ett steg längre och säga, "Varför skulle argv [1] segmenteringsfel?"
Låt oss skriva ut argv [1], och det ser ut som det är 0x0, som är NULL-pekare.
Vi strcmping CS50 stenar och noll, och så det kommer att segmenteringsfel.
Och varför är argv [1] null?
[Eleven] Eftersom vi inte gav det någon kommandoradsargument.
Ja. Vi gav inte det någon kommandoradsargument.
Så ./buggy1 bara kommer att ha argv [0] vara ./buggy1.
Det kommer inte att ha en argv [1], så det kommer att segmenteringsfel.
Men om man istället gör jag bara CS50, det kommer att säga att du får ett D
eftersom det är vad det är tänkt att göra.
Titta på buggy1.c, är det tänkt att skriva ut "Du får en D" -
Om argv [1] är inte "CS50 rocks", "Du får en D", annars "Du får ett A!"
Så om vi vill ha en A behöver vi detta för att jämföra som sant,
vilket innebär att det kan jämföras med 0.
Så argv [1] måste "CS50 rocks".
Om du vill göra det på kommandoraden, måste du använda \ fly utrymmet.
Så CS50 \ stenar och du får ett A!
Om du inte gör det omvända snedstrecket, varför detta inte att fungera?
[Elev] Det är två olika argument. >> Ja.
Argv [1] kommer att bli CS50 och argv [2] kommer att bli stenar. Okej.
>> Nu ./buggy2 ska segmenteringsfel igen.
Istället för att öppna den med sin kärna fil, vi öppnar bara upp buggy2 direkt,
så gdb buggy2.
Nu om vi bara kör vårt program, så det kommer att säga Program mottagen signal SIGSEGV,
som är segmenteringsfel signalen, och det är där det hände skulle hända.
Titta på vår bakåtspårning, ser vi att vi var i funktion oh_no,
som kallades av funktionen dinky, som kallades av funktionen Binky,
som kallades av huvud.
Vi kan också se argumenten för dessa funktioner.
Argumentet till dinky och Binky var 1.
Om vi ​​lista funktionen oh_no, ser vi att oh_no bara gör char ** s = null;
* S = "BOOM";
Varför skulle det inte?
[Eleven] Du kan inte dereference null pekaren? >> Ja.
Detta är bara att s är NULL, oavsett om det råkar vara en röding **
som, beroende på hur du tolkar det, kan det vara en pekare till en pekare till en sträng
eller en uppsättning av strängar.
Det är s är NULL så * s är dereferencing en nollpekare,
och så detta kommer att krascha.
Detta är en av de snabbaste sätten du kan möjligen segmenteringsfel.
Det är bara att förklara en null-pekare och omedelbart segfaulting.
Det är vad oh_no gör.
Om vi ​​går upp en ram, då vi kommer att få in den funktion som kallas oh_no.
Jag måste göra det ner.
Om du inte anger ett kommando och du träffar bara Enter igen,
det kommer bara upprepa föregående kommando som du körde.
Vi är i ram 1.
Listar denna ram ser vi här är vår uppgift.
Du kan slå listan igen, eller så kan du göra-lista 20 och det kommer att ge mer.
Funktionen Dinky säger att om jag är 1, sedan gå till oh_no funktionen
annars gå till Slinky funktionen.
Och vi vet att jag är 1 eftersom vi råkar se upp här
att Dinky kallades med argumentet 1.
Eller så kan du bara skriver jag och det kommer att säga att jag är 1.
Vi är för närvarande i dinky, och om vi går upp en annan ram, vi vet att vi kommer att hamna i Binky.
Upp. Nu är vi i Binky.
Listar denna funktion - listan från före halv avbröt mig -
Det började som om jag är 0, då ska vi kalla det oh_no, annars ringer dinky.
Vi vet att jag var 1, så kallas det dinky.
Och nu är vi tillbaka i main, och viktigaste är bara kommer att bli int i = rand ()% 3;
Som bara kommer att ge dig ett slumptal som är antingen 0, 1 eller 2.
Det kommer att kalla Binky med det numret, och det kommer tillbaka 0.
Titta på detta,
bara gå igenom programmet manuellt utan att köra det direkt,
du skulle sätta en brytpunkt på huvud, vilket innebär att när vi kör programmet
ditt program körs tills den träffar en brytpunkt.
Så kör programmet, kommer den att köra och så kommer det att slå huvudfunktionen och sluta köra.
Nu är vi inne i huvud och steg eller nästa kommer att föra oss till nästa kodrad.
Du kan göra steg eller nästa.
Utslagning nästa, nu jag har satts till rand ()% 3, så att vi kan skriva ut värdet av i,
och det kommer att säga att jag är 1.
Nu spelar ingen roll om vi använder nästa eller steg.
Jag antar att det betydde i den föregående, men vi skulle vilja använda härnäst.
Om vi ​​använder steg, vi steg in i funktionen, vilket innebär titta på själva saken
som händer inne i Binky.
Om vi ​​använder nästa, så betyder det att gå över funktionen
och bara gå vidare till nästa kodrad i vår huvudsakliga funktion.
Här på denna linje, jag var på där det stod rand ()% 3;
om jag gjorde steg, skulle det gå till genomförandet av rand
och titta på vad som händer där, och jag kunde gå igenom rand-funktionen.
Men jag bryr mig inte om funktionen rand.
Jag vill bara gå till nästa kodrad i huvud, så jag använder nästa.
Men nu gör jag bryr sig om Binky funktionen, så jag vill kliva in i det.
Nu är jag i Binky.
Den första raden i koden kommer att säga om (i == 0), jag tar ett steg,
Vi ser att vi hamnar på dinky.
Om vi ​​listar saker, ser vi att det kontrolleras är i = 0.
Jag är inte lika med 0, så det gick till annat tillstånd,
som kommer att ringa dinky (i).
Du kan bli förvirrad.
Om du bara tittar på dessa linjer direkt, kanske du tänker om (i == 0),
okej, så jag tog ett steg och nu är jag på dinky (i),
Ni kanske tror att måste betyda i = 0 eller något.
Nej, det betyder bara att det vet att det kan fastna direkt till linjen dinky (i).
Eftersom jag inte är 0, är ​​nästa steg kommer inte att sluta på annat.
Annars är inte en linje att det kommer att stanna vid.
Det är bara att gå till nästa rad kan det faktiskt utföra, vilket är dinky (i).
Kliva in dinky (i), ser vi om (i == 1).
Vi vet i = 1, så när vi steg, vi vet att vi kommer att hamna i oh_no
eftersom i = 1 anropar funktionen oh_no, som du kan kliva in,
som kommer att ställa char ** s = till NULL och omedelbart "BOOM".
Och sedan faktiskt titta på genomförandet av buggy2,
detta är jag få bara ett slumptal - 0, 1, eller 2 - ringer Binky,
som om jag är 0 man kallar oh_no, annars kallar Dinky kommer som hit.
Om jag är 1, ring oh_no, annars kalla slinky som kommer upp hit,
Om jag är 2, ring oh_no.
Jag tror inte ens att det finns ett sätt -
Ser någon ett sätt att göra detta ett program som inte kommer segmenteringsfel?
För om jag missat något, om jag är 0, kommer du segmenteringsfel omedelbart,
annat du gå till en funktion som om jag är en man segmenteringsfel,
annat du gå till en funktion där om jag är 2 du segmenteringsfel.
Så oavsett vad du gör, segmenteringsfel dig.
>> Jag antar att ett sätt att fastställa att det skulle vara istället för att göra char ** s = NULL,
du kan malloc utrymme för den raden.
Vi kan göra malloc (sizeof) - sizeof vad?
[Elev] (char) * 5? >> Verkar detta rätt?
Jag antar detta kommer att fungera om jag faktiskt körde det, men det är inte vad jag letar efter.
Titta på den typ av s. Låt oss lägga int *, så int * x.
Jag skulle göra malloc (sizeof (int)).
Eller om jag ville ha en uppsättning av 5, skulle jag göra (sizeof (int) * 5);
Vad händer om jag har en int **?
Vad skulle jag malloc?
[Elev] Storlek på pekaren. >> Ja. (Sizeof (int *));
Samma sak här nere.
Jag vill ha (sizeof (char *));
Detta kommer att allokera utrymme för pekaren som pekar på "BOOM".
Jag behöver inte allokera utrymme för "boom" själv
eftersom detta är i grunden motsvarar vad jag sa tidigare
av char * x = "BOOM".
"BOOM" finns redan. Det händer att existera i skrivskyddade region minne.
Men det finns redan, vilket innebär kodraden, om S är en röding **
då * s är en char * och du ställer denna char * peka på "BOOM".
Om jag ville kopiera "BOOM" i s, då skulle jag behöva allokera utrymme för s.
Jag gör * s = malloc (sizeof (char) * 5);
Varför 5?
Varför inte 4? Det ser ut som "boom" är 4 tecken. >> [Elev] Den null karaktär.
Ja. Alla dina strängar kommer att behöva null karaktär.
Nu kan jag göra något liknande strcat - Vad är funktionen för att kopiera en sträng?
[Eleven] CpY? >> Strcpy.
Mannen strcpy.
Så strcpy eller strncpy.
strncpy är lite säkrare eftersom du kan ange exakt hur många tecken,
men här spelar det ingen roll eftersom vi vet.
Så strcpy och titta i argumenten.
Det första argumentet är vårt mål.
Det andra argumentet är vår källa.
Vi kommer att kopiera till vår destination * s pekaren "BOOM".
Varför kan du göra detta med en strcpy istället för att bara vad vi hade innan
av * s = "BOOM"?
Det finns en anledning till att du kanske vill göra detta, men vad är den anledningen?
[Eleven] Om du vill ändra något i "boom". >> Ja.
Nu kan jag göra något liknande s [0] = 'X';
eftersom s pekar på högen och utrymme på högen som är pekar på
är en pekare till mer utrymme på högen, som lagrar "BOOM".
Så denna kopia av "boom" lagras i högen.
Det är tekniskt två exemplar av "boom" i vårt program.
Det är den första som bara ges av detta "BOOM" strängkonstant,
och den andra kopian av "boom", skapade strcpy kopian av "boom".
Men kopia av "boom" lagras på högen, och högen du är fri att ändra.
Högen är inte skrivskyddad innebär så att s [0]
kommer att låta dig ändra värdet på "BOOM".
Det kommer att låta dig ändra dessa tecken.
>> Frågor?
Okej.
>> Går vidare till buggy3, låt oss gdb buggy3.
Vi kör bara det och vi ser att vi får en segmenteringsfel.
Om vi ​​bakåtspårning finns det bara två funktioner.
Om vi ​​går upp i våran funktion, ser vi att vi segfaulted på denna linje.
Så bara att titta på denna linje, int rad = 0 för (, fgets det här inte lika NULL;
linje + +).
Vår tidigare bildruta kallades _IO_fgets.
Du ser att en hel del med inbyggd C funktioner,
att när du får segmenteringsfel kommer det bli riktigt kryptiska funktionsnamn
som detta _IO_fgets.
Men det kommer att relatera till detta fgets samtal.
Någonstans inne här, vi segfaulting.
Om vi ​​tittar på de argument fgets, kan vi skriva ut buffert.
Låt oss skriva ut som en - Åh, nej.
Print kommer inte att fungera precis som jag vill.
Låt oss titta på det aktuella programmet.
Buffert är ett tecken array. Det är ett tecken uppsättning av 128 tecken.
Så när jag säger utskriftsbufferten, det kommer att skriva ut dessa 128 tecken,
som jag antar är vad som förväntas.
Vad jag letar efter är skriva adressen till bufferten,
men som inte säger mig verkligen mycket.
Så när jag råkar säga upp här X buffert, visar mig 0xbffff090,
som, om du kommer ihåg från tidigare eller någon gång, tenderar Oxbffff att bli en stack-ish regionen.
Stacken tenderar att börja någonstans strax under 0xc000.
Bara genom att se denna adress, vet jag att bufferten sker på stacken.
Omstart mitt program, köra, upp, buffert vi såg var denna sekvens av tecken
som är ganska mycket meningslöst.
Sedan skriver fil, vad filen se ut?
[Eleven] Null. >> Ja.
Filen är en av typen FILE *, så det är en pekare,
och värdet av denna pekare är noll.
Så fgets ska försöka läsa från den pekare på ett indirekt sätt,
men för att komma åt den pekare, måste det dereference det.
Eller, för att få tillgång till vad den borde peka på, det dereferences det.
Så det är dereferencing en nollpekare och det segfaults.
Jag kunde ha startat det där.
Om vi ​​bryter på vår viktigaste punkten och kör,
den första raden i koden är char * filnamn = "nonexistent.txt";
Det borde ge en ganska stor ledtråd till varför detta program inte fungerar.
Skriva nästa leder mig till nästa rad, där jag öppnar den här filen,
och då jag genast komma in vår linje, där en gång jag slog nästa, det kommer att segmenteringsfel.
Vill någon att kasta ut en anledning till att vi kanske segfaulting?
[Elev] Filen existerar inte. >> Ja.
Detta är tänkt att vara en ledtråd
att när du öppnar en fil som du behöver för att kontrollera att filen existerar.
Så här, "nonexistent.txt";
När vi fopen filnamn för läsning, måste vi då säga
if (fil == NULL) och säg printf ("Filen finns inte!"
eller - ännu bättre - filnamn), avkastning 1;
Så nu vi kontrollera om det är NULL
innan faktiskt fortsätter och försöker läsa från den filen.
Vi kan göra om det bara för att se att det fungerar.
Jag avser att inkludera en ny rad.
Så nu nonexistent.txt inte existerar.
Du bör alltid kontrollera den här sortens saker.
Du bör alltid kontrollera om fopen returnerar null.
Du bör alltid kontrollera att se till att malloc inte returnerar NULL,
eller annat du segmenteringsfel.
>> Nu buggy4.c.
Igång. Jag gissar det väntar på inmatning eller möjligen oändlig loop.
Ja, det är oändligt looping.
Så buggy4. Det ser ut som vi är oändlig loop.
Vi kan bryta på huvud, kör vårt program.
I gdb, så länge förkortning du använder är entydig
eller speciella förkortningar som de tillhandahåller för dig,
då kan du använda n för att använda nästa i stället för att behöva skriva ut nästa hela vägen.
Och nu när jag har drabbats n en gång, kan jag slå bara Enter för att fortsätta nästa
istället för att behöva slå n Ange, n Ange, n Enter.
Det ser ut som jag är i någon form av for-slinga som lurar array [i] till 0.
Det ser ut som jag aldrig bryta sig ur detta för slinga.
Om jag skriver jag, så jag är 2, så jag ska gå härnäst.
Jag skriver jag, jag är 3, så jag ska gå härnäst.
Jag skriver jag och jag är 3. Därefter skriver jag, i är 4.
Egentligen är print sizeof (matris), så storlek matris 20.
Men det ser ut som det finns några speciella gdb-kommandot för att gå tills något händer.
Det är som att ställa ett villkor på värdet av variabeln. Men jag minns inte vad det är.
Så om vi hålla -
Vad var det du sa? Vad tog du med dig?
[Elev] Har visar jag tillägga - >> Ja. Så visar jag kan hjälpa.
Om vi ​​bara visa i, kommer det att sätta upp här vad värdet av i är
så jag behöver inte skriva ut varje gång.
Om vi ​​bara hålla kommer nästa ser vi 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5.
Något går fruktansvärt fel, och jag håller på att återställas till 0.
Titta på buggy4.c ser vi allt som händer är int array [5];
for (i = 0; i <= sizeof (matris), i + +)
array [i] = 0;
Vad ser vi att det är fel här?
Som en ledtråd, när jag gör gdb buggy4 - låt oss break, run -
Jag skrevs ut sizeof (matris) bara för att se vad tillståndet är där jag bör slutligen bryta ut.
Var är jag? Har jag springa?
Jag har inte deklarera ännu.
Så ut sizeof (matris) och det är 20,
som förväntas eftersom min array är av storlek 5 och det är av 5 heltal,
så det hela ska vara 5 * sizeof (int) byte, där sizeof (int) tenderar att vara 4.
Så sizeof (matris) är 20.
Vad ska det vara?
[Elev] dividerat med sizeof (int). >> Ja, / sizeof (int).
Det ser ut som det är fortfarande ett problem här. Jag tror att detta bara ska vara <
eftersom det är ganska mycket alltid <och aldrig <=.
Nu ska vi fundera över varför det var faktiskt bruten.
Har någon gissningar varför var jag återställa till 0 genom varje iteration av slingan?
Det enda inuti här som händer är att array [i] ställs in på 0.
Så på något sätt är denna kodrad som orsakar vår int i sättas till 0.
[Elev] Kan det vara för att det övergripande minnet av denna del av i
när den anser att det är nästa del av matris? >> [Bowden] Ja.
När vi går utöver slutet av vår grupp,
något sätt att utrymme som vi tvingande är tvingande värde i..
Och så om vi ser på buggy4, break, springa,
låt oss skriva adressen till i..
Det ser ut som det är bffff124.
Nu ska vi skriva adressen till arrayen [0]. 110.
Hur är [1]? 114.
[2], 118.
11c, 120. Array [5] är bfff124.
Så Array [5] har samma adress som jag, vilket innebär att Array [5] är jag.
Om de har samma adress, är de samma sak.
Så när vi satt array [5] till 0, sätter vi i till 0.
Och om du tänker på detta i form av stapeln,
int i förklaras först, vilket betyder att jag får lite utrymme på stacken.
Då Array [5] fördelas, så då 20 byte tilldelas på stacken.
Så jag får fördelas i första, då dessa 20 byte blir tilldelade.
Så jag händer precis innan arrayen,
och på grund av det sätt, som jag sa förra veckan, där det är tekniskt stapeln växer ner,
när man index i en matris, är vi garanteras att 0:e position i matrisen
alltid händer innan den första positionen i arrayen.
Detta är typ av hur jag drog det förra veckan.
Lägg märke till att vid botten har vi adress 0 och vid toppen har vi adress.
Stacken är alltid växer nedåt.
Låt oss säga att vi fördela i..
Vi fördela heltal i, vilket betyder låt oss bara säga upp här heltal i blir tilldelade.
Då vi fördela vår samling av 5 heltal, vilket innebär att under det,
eftersom stapeln växer ner, få de 5 heltal tilldelas.
Men på grund av hur arrayer fungerar, vi garanterat att den första positionen i arrayen
alltid har en adress mindre än den andra sak i arrayen.
Så array läge 0 måste alltid ske först i minnet,
medan array läge 1 måste ske efter det
och array läge 2 måste ske efter det,
vilket innebär att array läge 0 skulle hända någonstans här nere,
matrisposition 1 skulle hända ovan att
eftersom flytta upp innebär högre adresser eftersom den högsta adressen är här uppe.
Så array [0] här nere, array [1] upp här, array [2] här uppe, array [3] upp här.
Lägg märke till hur innan vi tilldelades heltal i hela vägen upp hit,
när vi går längre och längre in i vår grupp, vi komma närmare och närmare vår heltal i..
Det råkar vara så att arrayen [5], som är en plats bortom vår array,
är precis där heltal jag råkade fördelas.
Så det är den punkt där vi råkar slå utrymmet på stacken
som avsatts för heltal i, och vi sätta det till 0.
>> Det är så det fungerar. Frågor? Ja.
[Elev] Strunta. Okej.
[Elev] Hur undviker man denna typ av fel?
Denna typ av fel? Använd inte C som din programmeringsspråk.
Använd ett språk som har array gränskontroll.
Så länge du är försiktig, behöver du bara undvika att gå förbi gränserna för din array.
[Eleven] Så här när vi gick förbi gränserna för din array -
[Bowden] Det är där saker och ting börjar gå snett. >> [Elev] Åh, okej.
Så länge du håller dig i minnet som tilldelats för din array, du bra.
Men C gör ingen felkontroll. Om jag gör array [1000], kommer det gärna bara ändra vad som än händer -
Det går till början av arrayen, så det går 1000 positioner efter och ställer den till 0.
Det gör inte någon kontroll som oh, detta inte faktiskt 1000 saker i den.
1000 är långt utöver vad jag borde förändras,
medan Java eller något du får utbud av gränserna index
eller index utanför gränserna undantag.
Det är därför en hel del på högre nivå språk har dessa saker
där om du går utöver gränserna för matrisen, misslyckas du
så att du inte kan ändra saker från under dig
och sedan det går mycket värre än att bara få ett undantag
säger att du gick längre än till slutet av arrayen.
[Elev] Och så borde vi just ändrat <= bara <? >> [Bowden] Ja.
Det bör vara <sizeof (matris) / sizeof (int);
eftersom sizeof (matris) är 20, men vi vill bara 5. >> [Elev] Rätt.
Fler frågor? Okej.
>> [Elev] Jag har en fråga. >> Ja.
[Eleven] Vad är den verkliga arrayvariabel?
[Bowden] Som vad är array?
Array själv är en symbol.
Det är bara adressen till början av 20 byte som vi refererar.
Du kan se det som en pekare, men det är en konstant pekare.
Så snart saker blir sammanställts existerar variabeln arrayen inte längre.
[Elev] Så hur hittar den storlek array?
Storlek på matris hänvisar till storleken på detta block att den symbolen hänvisar till.
När jag gör något liknande printf ("% p \ n", array);
låt oss köra den.
Vad har jag gjort bara fel?
Array "array" redovisas här.
Åh, här uppe.
Klang är smart, och det händer att märka att jag förklarade matrisen som 5 element
men jag indexera på plats 1000.
Det kan göra det eftersom det är bara konstanter.
Det kan bara gå så långt i att märka att jag kommer förbi gränserna för matrisen.
Men märker innan när vi hade jag vara felaktig,
Det kan omöjligen avgöra hur många värden jag kunde ta på,
så det kan inte bestämma att jag gick längre än till slutet av arrayen.
Det är bara klang är smart.
>> Men nu gör buggy4. Så vad mer gör jag för fel?
Underförstått förklara biblioteksfunktion "printf".
Jag kommer att vilja # include .
Okej. Nu kör buggy4.
Skriva ut värdet i matrisen som jag gjorde här, skriva ut det som en pekare
utskrifter något som ser ut så här - bfb8805c - som är en del adress
Det är i bunten-ish regionen.
Array själv är som en pekare, men det är inte en faktisk pekare,
eftersom en vanlig pekare som vi kan förändra.
Array är bara några konstant. De 20 minnesblock börja adress 0xbfb8805c.
Så bfb8805c genom denna adress +20--eller jag antar -20 -
är allt minne allokeras för denna grupp.
Array, är själva variabeln lagras inte någonstans.
När du kompilerar, kompilatorn - hands våg på det -
men kompilatorn kommer bara använda när den vet array för att vara.
Det vet var den arrayen börjar,
och så det kan alltid bara göra saker när det gäller förskjutningar från början.
Det behöver inte en variabel sig för att representera array.
Men när jag gör något som int * p = array, nu p är en pekare som pekar på den arrayen,
och nu p faktiskt existerar på stacken.
Jag är fri att ändra sid. Jag kan göra p = malloc.
Så det pekade ursprungligen rad, nu pekar på en viss plats på högen.
Jag kan inte göra array = malloc.
Om klang är smart, kommer det att skrika på mig just off the bat.
Jag är faktiskt ganska säker gcc skulle göra detta också.
Så array typ "int [5]" är inte överlåtas.
Du kan inte tilldela något till en array typ
eftersom matrisen är bara en konstant.
Det är en symbol som hänvisar dessa 20 byte. Jag kan inte ändra det.
>> [Elev] Och där är storleken på matrisen lagras?
[Bowden] Det är inte lagras någonstans. Det är när det kompilering.
Så där är storleken på matrisen lagras?
Du kan bara använda sizeof (matris) inuti den funktion som arrayen är förklarat sig.
Så om jag gör någon funktion, foo, och jag gör (int array [])
printf ("% d \ n", sizeof (matris));
och sedan ner här jag kallar foo (matris);
insidan av denna funktion - låt oss köra den.
Detta är klang är smart igen.
Det säger mig att sizeof på rad funktionsparameter
kommer tillbaka storleken på "int * '.
Detta skulle vara ett misstag om det inte det jag ville skulle hända.
Låt oss verkligen stänga Werror.
Varning. Varningar är bra.
Det kommer fortfarande sammanställer så länge som den har en varning.
. / A.out kommer att skriva ut 4.
Varningen som genererades är en tydlig indikator på vad som gick fel.
Denna int array bara att skriva ut sizeof (int *).
Även om jag sätter array [5] här, det är fortfarande bara att skriva ut sizeof (int *).
Så fort du för in en funktion, skillnaden mellan arrayer och pekare
är obefintlig.
Det råkar vara en array som förklarades på stacken,
men så snart vi passerar det värdet att 0xbf bla, bla, bla i denna funktion,
då denna pekare pekar på den arrayen på stacken.
Så det betyder att sizeof endast gäller den funktion som arrayen förklarades,
vilket innebär att när du kompilerar denna funktion,
När klang går igenom den här funktionen ser det array är en int array av storlek 5.
Så då ser sizeof (matris). Tja, det är 20.
Det är faktiskt hur sizeof princip fungerar för nästan alla fall.
Sizeof är inte en funktion, det är en operatör.
Du kallar inte sizeof funktionen.
Sizeof (int) kommer kompilatorn översätter bara det till 4.
Uppfattat? Okej.
>> [Elev] Så vad är skillnaden mellan sizeof (matris) i huvud och foo!
Detta beror på att vi säger sizeof (matris), som är av typen int *,
medan matrisen här nere är inte av typen int *, det är en int array.
>> [Elev] Så om du hade parametern i arrayen [] istället för int * array,
skulle det betyda att du fortfarande kan ändra uppsättning för nu är det en pekare?
[Bowden] Gillar du? >> [Elev] Ja. Kan du ändra rad i funktionen nu?
[Bowden] Du kan ändra rad i båda fallen.
I båda dessa fall är du fri att säga array [4] = 0.
[Elev] Men kan du göra array pekar på något annat?
[Bowden] Oh. Ja. I båda fallen - >> [elev] Ja.
[Bowden] Skillnaden mellan Array [] och en int * array, det finns ingen.
Du kan också få lite flerdimensionell array här
för några praktiska syntax, men det är fortfarande bara en pekare.
Det innebär att jag är fri att göra array = malloc (sizeof (int)), och nu pekar någon annanstans.
Men precis som hur det fungerar alltid och alltid,
ändra denna rad genom att göra det pekar på något annat
ändrar inte denna array här nere eftersom det är en kopia av argumentet,
det är inte en pekare till det argumentet.
Och faktiskt, precis som mer tecken på att det är exakt samma -
vi såg redan vad utskrifter utskrift array -
Vad händer om vi skriver ut adressen till gruppen eller adressen till adressen i matrisen
till någon av dem?
Låt oss bortse från detta.
Okej. Detta är bra. Det är nu igång. / A.out.
Utskrift matris, sedan skriva adressen till arrayen, är samma sak.
Array bara inte.
Den vet när du skriver array, du skriver den symbol som hänvisar till dessa 20 byte.
Skriva ut adress arrayen, ja, existerar array inte.
Det har inte en adress, så att det bara skriver adressen för dessa 20 bitgrupper.
Så snart du kompilerar ner, som i din sammanställt buggy4. / A.out,
matris är obefintlig.
Pekare finns. Matriser inte.
Blocken minne representerar matrisen finns fortfarande,
men den variabla matris och variabler av denna typ inte finns.
De är som de viktigaste skillnaderna mellan arrayer och pekare
är så fort du gör funktionsanrop, är det ingen skillnad.
Men insidan av funktion som arrayen självt förklaras arbetar sizeof annorlunda
eftersom du skriver storleken på blocken istället för storleken på typen,
och du kan inte ändra det eftersom det är en symbol.
Skriva ut saken och adressen saken skriver samma sak.
Och det är ganska mycket det.
[Elev] Kan du säga att en gång till?
Jag kanske har missat något.
Utskrift array och adress matris skriver samma sak,
medan om du skriver ut en pekare mot adressen till pekaren,
en sak skriver adressen till vad du pekar på,
den andra skriver adressen till pekaren på stacken.
Du kan ändra en pekare, du kan inte ändra en rad symbol.
Och sizeof pekare kommer att skriva ut storleken på den pekartyp.
Så int * p sizeof (p) kommer att skriva ut 4,
men int array [5] Skriv ut sizeof (matris) kommer att skriva ut 20.
[Elev] Så int array [5] kommer att skriva ut 20? >> Ja.
Det är därför inne i buggy4 när det brukade vara sizeof (matris)
detta gjorde i <20, vilket inte är vad vi ville ha.
Vi vill i <5. >> [Elev] Okej.
[Bowden] Och sedan så fort du börjar passerar i funktionerna,
om vi gjorde int * p = array;
insidan av denna funktion, kan vi använda i stort sett p och matris i exakt samma sätt,
med undantag för sizeof problemet och växande problem.
Men p [0] = 1, är detsamma som att säga array [0] = 1;
Och så snart vi säger foo (matris) eller foo (p);
insidan av foo funktionen är samma samtal två gånger.
Det är ingen skillnad mellan dessa två samtal.
>> Alla bra på det? Okej.
Vi har 10 minuter.
>> Vi ska försöka få igenom detta Hacker Typer program
denna webbplats, som kom ut förra året eller något.
Det är bara tänkt att vara som du skriver slumpmässigt och det skrivs ut -
Oavsett fil det händer att ha laddat är vad det ser ut som du skriver.
Det ser ut som något slags operativsystem kod.
Det är vad vi vill genomföra.
Du bör ha en binär körbar heter hacker_typer
som tar i ett enda argument, filen till "hacker-typ."
Köra den körbara ska rensa skärmen
och sedan skriva ut ett tecken från den skickade i fil varje gång användaren trycker på en tangent.
Så vad tangent du trycker på, bör det kasta bort och istället skriva ut ett tecken från filen
som är argumentet.
Jag ska ganska mycket berätta vad de saker vi kommer att behöva veta är.
Men vi vill att checka ut termios biblioteket.
Jag har aldrig använt det här biblioteket i hela mitt liv, så det har mycket minimal syften.
Men detta kommer att bli biblioteket kan vi använda för att kasta bort det tecken du träffar
När du skriver till standard i.
Så hacker_typer.c, och vi kommer att vilja # include .
Man tittar på man-sidan för termios - jag gissar att det terminal OS eller något -
Jag vet inte hur man läser det.
Titta på det här, säger den till att omfatta dessa 2 filer, så vi ska göra det.
>> Första sak först, vill vi ta i ett enda argument, som är den fil som vi skall öppna.
Så vad vill jag göra? Hur kontrollerar jag att se jag har ett enda argument?
[Eleven] Om argc lika det. >> [Bowden] Ja.
Så om (argc = 2!) Printf ("Användning:% s [fil som ska öppnas]").
Så nu om jag kör det här utan att ge en andra argument - åh, jag behöver den nya linjen -
ser du det står användning:. / hacker_typer,
och sedan det andra argumentet bör vara den fil jag vill öppna.
Vad gör jag?
Jag vill läsa från filen. Hur läser jag från en fil?
[Elev] Du öppnar den första. >> Ja.
Så fopen. Hur ser fopen ut?
[Elev] Filnamn. >> [Bowden] Filnamn kommer att bli argv [1].
[Elev] och sedan vad du vill göra med det, så - >> [Bowden] Ja.
Så om du inte kommer ihåg, kan du göra precis människa fopen,
där det kommer att bli en const char * sökväg där sökväg är filnamn,
const char *-läge.
Om du råkar inte komma ihåg vad läget är, då kan du leta efter läge.
Inuti man-sidor är snedstreck vad du kan använda för att söka efter saker.
Så jag skriver / läge för att söka efter läge.
n och n är vad du kan använda för att bläddra igenom sökning matcher.
Här står de punkter argument läget till en sträng
början med en av följande sekvenser.
Så r, Öppna textfil för läsning. Det är vad vi vill göra.
För att läsa, och jag vill spara det.
Saken kommer att bli en fil *. Nu vad vill jag göra?
Ge mig en sekund.
Okej. Nu vad vill jag göra?
[Elev] Kontrollera om det är NULL. >> [Bowden] Ja.
Varje gång du öppnar en fil, se till att du framgångsrikt kunna öppna den.
>> Nu vill jag göra det termios saker där jag vill först läsa mina nuvarande inställningar
och spara dem till något, då jag vill ändra mina inställningar
att kasta bort alla tecken som jag skriver,
och då vill jag uppdatera dessa inställningar.
Och sedan i slutet av programmet, vill jag ändra tillbaka till min ursprungliga inställningar.
Så struct kommer att vara av typ termios, och jag kommer att vilja två av dem.
Den första kommer att vara mina current_settings,
och då de kommer att vara mina hacker_settings.
Först kommer jag att vilja spara mina nuvarande inställningar,
då ska jag vill uppdatera hacker_settings,
och sedan sätt i slutet av mitt program vill jag återgå till de aktuella inställningarna.
Så sparar aktuella inställningar, det sätt som fungerar, vi man termios.
Vi ser att vi har denna int tcsetattr, int tcgetattr.
Jag passerar en termios struct av dess pekare.
Hur denna kommer att se är - jag har redan glömt vad funktionen hette.
Kopiera och klistra in den.
Så tcgetattr, då vill jag gå i struct som jag sparar informationen i,
som kommer att bli current_settings,
och det första argumentet är filidentifieraren för det jag vill spara attribut.
Vad filidentifieraren är är som när du öppnar en fil, det blir en filidentifierare.
När jag fopen argv [1], det blir en filidentifierare som du refererar
när du vill läsa eller skriva till den.
Det är inte filidentifieraren jag vill använda här.
Det finns tre filbeskrivningar du har som standard,
som är standard i, standard ut, och standardfel.
Som standard, jag tror det är standard i är 0, är ​​standard ut 1, och standard error är 2.
Så vad vill jag ändra inställningarna för?
Jag vill ändra inställningarna för varje gång jag slog ett tecken,
Jag vill att det ska kasta tecknet bort i stället för att skriva ut det på skärmen.
Vilken ström - standard i, standard ut, eller standardfel -
svarar på saker när jag skriver på tangentbordet? >> [Eleven] Standard i. >> Ja.
Så jag kan antingen göra 0 eller jag kan göra stdin.
Jag får current_settings av standard i.
>> Nu vill jag uppdatera dessa inställningar,
så först ska jag kopiera till hacker_settings vad mina current_settings är.
Och hur structs arbete är det bara kopiera.
Detta kopierar alla fält, som du förväntar dig.
>> Nu vill jag uppdatera några av fälten.
Titta på termios, skulle du behöva läsa igenom en hel del av detta
bara för att se vad du vill söka efter,
men flaggorna du kommer att vilja leta efter är eko,
så ECHO ECHO inmatade tecken.
Först vill jag ställa - jag har redan glömt vad fälten.
Detta är vad struct ser ut.
Så inmatningslägen Jag tror att vi vill ändra.
Vi ska titta på lösningen för att se till att det är vad vi vill förändra.
Vi vill ändra lflag för att förhindra behöva titta igenom alla dessa.
Vi vill förändra lokala lägen.
Du skulle behöva läsa igenom hela denna sak att förstå där allt hör
att vi vill förändra.
Men det är inne i lokala lägen där vi kommer att vilja ändra på det.
Så hacker_settings.cc_lmode är vad det kallas.
c_lflag.
Det är där vi kommer in bitvisa operatörer.
Vi typ av för sent, men vi ska gå igenom det riktigt snabbt.
Det är där vi kommer in bitvisa operatörer,
där jag tror att jag sa en gång för länge sedan att när du börjar att göra med flaggor,
du kommer att använda bitvis mycket.
Varje bit i flaggan motsvarar någon form av beteende.
Så här har denna flagga en massa olika saker, där alla av dem betyder något annat.
Men vad jag vill göra är att stänga precis utanför bit som motsvarar ECHO.
Så för att vända det ut jag & = ¬ ECHO.
Egentligen tycker jag att det är som techo eller något. Jag ska bara kolla igen.
Jag kan termios den. Det är ECHO bara.
ECHO kommer att bli en enda bit.
¬ ECHO kommer att betyda alla bitar är satta till 1, vilket innebär att alla flaggor som är inställda på true
med undantag för ECHO lite.
Genom att avsluta min lokala flaggor med detta innebär det flagg som för närvarande satt till TRUE
kommer fortfarande att anges till true.
Om min ECHO flaggan är satt till true, då nödvändigtvis inställd till false på ECHO flaggan.
Så här kodraden stänger bara av ECHO flaggan.
De andra kodrader, jag bara kopiera dem till förmån för tid och sedan förklara dem.
I lösningen, sade han 0.
Det är nog bättre att uttryckligen säga stdin.
>> Lägg märke till att jag också ska göra ECHO | icanon här.
Icanon avser något separat, vilket innebär kanoniska läge.
Vilka kanoniska läge betyder är oftast när du skriver ut kommandoraden,
standard i bearbetar inte något förrän du träffar newline.
Så när du gör getString skriver du en massa saker, då du träffar newline.
Det är när det är skickat till standard i.
Det är standard.
När jag stänger av kanoniska läge nu varje enskilt tecken du trycker
är vad som får behandlas, som vanligtvis är typ av dåligt eftersom det är långsamt att bearbeta dessa saker,
varför det är bra att buffra den till hela linjer.
Men jag vill varje tecken som skall behandlas
eftersom jag vill inte att det ska vänta på mig att slå newline
innan bearbetningen alla tecken jag har att skriva.
Detta stänger kanoniska läge.
Här grejer betyder bara när det faktiskt bearbetar tecken.
Detta innebär bearbeta dem omedelbart, så snart jag skriver dem, bearbeta dem.
Och detta är den funktion som uppdaterar mina inställningar för standard i,
och TCSA medel göra det just nu.
De andra alternativen är vänta tills allt som är närvarande på strömmen behandlas.
Det spelar ingen roll egentligen.
Just nu ändra mina inställningar för att vara vad är för närvarande i hacker_typer_settings.
Jag antar att jag kallade det hacker_settings, så låt oss ändra på det.
Ändra allt till hacker_settings.
>> Nu i slutet av vårt program vi kommer att vilja återgå
till vad som för närvarande inne i normal_settings,
som kommer att bara se ut och normal_settings.
Märker jag har inte ändrat någon av mina normal_settings sedan början få det.
Sen att bara ändra dem tillbaka, passerar jag dem tillbaka i slutet.
Detta var uppdateringen. Okej.
>> Nu inne i här jag ska bara förklara koden av intresse för tiden.
Det är inte så mycket kod.
Vi ser att vi läser ett tecken från filen. Vi kallade det f..
Nu kan du människan fgetc, men hur fgetc kommer att fungera
är bara det kommer att returnera det tecken som du just läst eller EOF,
vilket motsvarar slutet av filen eller några fel händer.
Vi looping, fortsätter att läsa ett tecken från filen,
tills vi har slut på tecken att läsa.
Och medan vi gör det, väntar vi på en enda karaktär från standard i.
Varje gång du skriver något på kommandoraden,
som är läsning i en karaktär från standard i.
Sedan putchar är bara att sätta röding vi läser upp här från filen till standard ut.
Du kan människan putchar, men det är bara att sätta standard ut, det du skriver tecken.
Du kan också bara göra printf ("% c", c), samma idé.
Det kommer att göra det mesta av vårt arbete.
>> Det sista vi kommer att vilja göra är fclose vår fil.
Om du inte fclose, det är en minnesläcka.
Vi vill fclose filen vi ursprungligen öppnade, och jag tror det är det.
Om vi ​​gör det, fick jag redan problem.
Låt oss se.
Vad gjorde det klaga?
Förväntad "int" men argumentet är av typen "struct _IO_FILE * '.
Vi får se om det fungerar.
Tillåts endast i C99. Augh. Okej, gör hacker_typer.
Nu får vi mer användbara beskrivningar.
Så använd odeklarerat identifierare "normal_settings".
Jag har inte kalla det normal_settings. Jag kallade det current_settings.
Så låt oss ändra på detta.
Nu passerar argument.
Jag ska göra det här 0 för nu.
Okej. . / Hacker_typer cp.c.
Jag också inte rensa skärmen från början.
Men du kan se tillbaka på det sista problemet inställda för att se hur du rensar skärmen.
Det är bara skriva några tecken
medan detta gör vad jag vill göra.
Okej.
Och tänka på varför detta behövs för att 0 istället för standard in,
som bör # define 0,
detta klagar på att -
Innan när jag sa att det finns fil deskriptorer men då har du också din fil *,
en fil deskriptor är bara en enda heltal,
medan en fil * har en hel massa saker i samband med detta.
Anledningen till att vi måste säga 0 istället för stdin
är att stdin är en fil * som pekar på det som är refererar filidentifierare 0.
Så även här uppe när jag gör fopen (argv [1], jag får en fil * tillbaka.
Men någonstans i filen * är en sak motsvarar filidentifieraren för filen.
Om man tittar på man-sidan för öppen, så jag tror du måste göra människan 3 öppen - nix -
Man 2 öppna - ja.
Om du tittar på sidan för öppen är öppen som en lägre nivå fopen,
och det är tillbaka själva filidentifierare.
fopen gör en massa saker ovanpå öppen,
som i stället för att återvända bara den filen deskriptor returnerar en hel fil * pekare
inuti som är vår lilla filidentifierare.
Så standard avser FIL * sak,
medan 0 avser bara filidentifieraren standarden i sig.
>> Frågor?
[Skrattar] blåste genom det.
Okej. Vi är klara. [Skrattar]
>> [CS50.TV]