[Powered by Google Translate] [AVSNITT 5: mindre bekväm] [Nate Hardison, Harvard University] [Detta är CS50.] [CS50.TV] Så välkommen tillbaka, grabbar. Välkommen till avsnitt 5. Vid denna punkt, efter att ha avslutat quiz 0 och efter att ha sett hur du har gjort, förhoppningsvis du känner riktigt bra eftersom jag var mycket imponerad av poängen i detta avsnitt. För våra online tittare, vi har haft ett par frågor om de sista två problemen på problemet set - eller på frågesport, snarare. Så vi kommer att gå över dem verkligen snabbt så att alla ser vad som hände och hur man ska gå igenom den faktiska lösning snarare än bara tittar själva lösningen. Vi kommer att gå under de senaste par problem verkligen snabbt, 32 och 33. Bara återigen, så att online tittarna se detta. Om du vänder dig till ditt problem 32, som finns på sidan 13, 13 av 16, är problemet 32 ​​handlar om swappar. Det handlade om att byta två heltal. Det är problem som vi hade gått över ett par gånger i föreläsningen. Och här, vad vi ber dig att göra är en sammanfattning av minne spår. Att fylla i värdena på variablerna som de är på stacken som koden går igenom denna swap funktion. I synnerhet vad vi tittar på - jag kommer att sätta detta iPad ned - i synnerhet, vad vi tittar på är denna linje numrerade 6 här. Och det är numrerade 6 för bara omedelbar närhet med föregående problemet. Vad vi vill göra är att visa eller märka tillstånd minnet eftersom det är vid den tidpunkt när vi utför denna linje nummer 6, vilket i själva verket är en återgång från vår swap-funktion här. Om vi ​​rulla ner hit såg vi att adresserna på allt i minnet lämnades för oss. Detta är mycket viktig, vi kommer tillbaka till det på bara ett ögonblick. Och sedan ner här på botten, hade vi en liten minne diagram som vi kommer att hänvisa till. Jag har faktiskt gjort detta på min iPad. Så jag ska alternera fram och tillbaka mellan iPad och denna kod bara som referens. Låt oss börja. Först, låt oss fokusera på den första par rader viktigaste här. Till att börja, vi kommer att initiera x till 1 och y till 2. Så vi har två heltalsvariabler, de är båda kommer att placeras på stacken. Vi ska sätta en 1 och en 2 i dem. Så om jag vänder över till min iPad, förhoppningsvis, låt oss se - Apple TV spegling, och där vi går. Okej. Så om jag vänder över till min iPad, Jag vill initiera x till 1 och y till 2. Vi gör det helt enkelt genom att skriva en 1 i rutan X och en 2 i rutan Y. Ganska enkel. Så låt oss nu gå tillbaka till den bärbara datorn, se vad som händer härnäst. Så detta nästa rad är där det blir knepigt. Vi passerar adress x och adress y som parametrarna a och b till swap-funktionen. Adressen x och adressen till y är saker som vi inte kan beräkna utan att hänvisa till dessa punktlistor just här nere. Och lyckligtvis de första två punktlistor berätta exakt vad svaren är. Adressen för x i minnet är 10, och adressen av y i minnet är 14. Så de är de värden som får skickas in som a och b uppe i vår swap-funktion. Så återigen, byta tillbaka till vår diagrammet, kan jag skriva en 10 i en och en 14 i b. Nu är denna punkt där vi vidare med swappen. Så vända tillbaka till den bärbara datorn igen, Vi ser att det sätt swappen fungerar är jag först dereference en och lagra resultatet i tmp. Så dereference operatören säger, "Hej. Behandla innehållet i variabel A som en adress. Gå till allt som lagras på den adressen, och ladda den. " Vad du laddar ur variabeln kommer att lagras i vår TMP variabel. Vända tillbaka till iPad. Om vi ​​går att lösa 10 vet vi att adress 10 är varible x eftersom vi fick höra av vår punktsatsen att adressen för x i minnet är 10. Så vi kan åka dit, få värdet av det, vilket är 1, som vi ser på vår iPad, och ladda det till TMP. Återigen, detta är inte den slutliga innehållet. Vi kommer att gå igenom och vi får till vår slutliga tillstånd av programmet i slutet. Men just nu har vi värdet 1 lagras i tmp. Och det finns en snabb fråga här. [Alexander] Är dereference operatör - det är bara stjärnan mitt framför variabeln? >> Ja. Så dereference operatören, som vi vänder tillbaka till våra bärbara gång, är denna stjärna rakt framför. I den meningen är det - du kontrastera det med multiplikation operatören som kräver två saker, den dereference operatören är en unär operatör. Bara tillämpas på ett värde i motsats till en binär operator, där du ansöker till två olika värden. Så det är vad som händer i denna linje. Vi laddade värdet 1 och lagras den i vår tillfälliga heltalsvariabel. Nästa rad lagrar vi innehållet i B till - eller snarare lagrar vi innehållet att b pekar på den plats där en pekar på. Om vi ​​analyserar detta från höger till vänster, kommer vi att dereference b, vi kommer att ta itu med 14, kommer vi att ta tag i heltal som finns där, och då kommer vi att gå till adressen 10, och vi kommer att kasta resultatet av vårt dereference av b i det utrymmet. Vända tillbaka till vår iPad, där vi kan göra det lite mer konkret, det kan hjälpa om jag skriver siffrorna på alla adresser här. Så vi vet att vid y, vi är på adress 14, x på adress 10. När vi börjar på B, vi dereference B, kommer vi att ta värdet 2. Vi kommer att ta tag i detta värde eftersom det är det värde som bor på adress 14. Och vi kommer att sätta in den variabel som bor på adress 10, som är rätt där, motsvarande våra variabel x. Så vi kan göra lite för att skriva över här där vi bli av med våra 1 och i stället skriver vi en 2. Så alla är väl och gott i världen, trots att vi har överskrivna X nu. Vi har sparat x gamla värde i vårt TMP variabel. Så vi kan slutföra växlingen med nästa rad. Vända tillbaka till vår laptop. Nu återstår är att ta innehållet ur vår tillfälliga heltalsvariabel och lagra dem i den variabel som bor på den adress som b håller. Så vi kommer att effektivt dereference B för att få tillgång till variabeln som är den adress som b håller i det, och vi kommer att stoppa det värde som TMP håller i den. Vända tillbaka till IPAD gång. Jag kan radera detta värde här, 2, och istället ska vi kopiera 1 rakt in i det. Sedan nästa rad som kör, naturligtvis - Om vi ​​vänder tillbaka till den bärbara datorn - är detta 6, vilket är den punkt där vi ville ha vår diagrammet helt fylld ut. Så vända tillbaka till IPAD gång, bara så att du kan se det färdiga diagrammet, du kan se att vi har en 10 i en, en 14 i b, en 1 i tmp, en 2 i x, och en 1 i y. Finns det några frågor om detta? Gör detta mer meningsfullt att ha gått igenom det? Gör mindre betydelse? Förhoppningsvis inte. Okej. Pekare är ett mycket svårt ämne. En av killarna vi arbetar med har ett mycket vanligt talesätt: "För att förstå pekare, måste du först förstå pekare." Vilket jag tycker är väldigt sant. Det tar ett tag att vänja sig. Rita massor av bilder, drar massor av minne diagram som detta mycket bra, och efter att du går igenom exempel efter exempel efter exempel, det ska börja göra lite mer känsla och lite mer känsla och lite mer känsla. Slutligen, en dag kommer du ha allt helt behärskar. Eventuella frågor innan vi går vidare till nästa problem? Okej. Så vänder tillbaka till den bärbara datorn. Nästa problem vi har är problem nummer 33 på fil-I / O. Zooma in på detta lite. Problem 33 - Ja? [Daniel] Jag hade bara en snabb fråga. Denna stjärna eller asterisk, det kallas dereferencing när du använder en asterisk före. Vad kallas det när du använder et-tecken innan? >> Et-tecknet är före adress-av operatör. Så låt oss rulla tillbaka. Oops. Jag är i zoomläge så jag kan inte riktigt rulla. Om vi ​​tittar på den här koden verkligen snabbt just här, igen, samma sak händer. Om vi ​​tittar på den här koden här, på denna linje där vi ringa för att byta, et-tecknet är bara att säga "få den adress där variabel x liv." När din kompilator kompilerar koden, det har faktiskt fysiskt markera en plats i minnet för alla dina variabler att leva. Och så vad kompilatorn kan då göra när det sammanställs allt, den vet, "Åh, jag satte X på adressen 10. Jag satte y på adress 14." Den kan sedan fylla i dessa värden för dig. Så du kan då - det kan då passera detta och Pass & Y i också. Dessa killar få adressen, men också, när du passerar dem i swap-funktion, denna typ information här int * här, berättar kompilatorn, "Okej, vi kommer att tolka denna adress som en adress för en heltalsvariabel." Som en adress för en int, vilket skiljer sig från adressen för ett tecken variabel eftersom en int tar upp, på en 32-bitars maskin, tar upp 4 byte av utrymme, medan en karaktär tar bara upp 1 byte utrymme. Så det är viktigt att veta också vad som är - vad lever, vilken typ av värde bor på den adress som fick skickas in Eller den adress som du arbetar med. På så sätt vet du hur många byte av information att faktiskt ladda ur ditt RAM. Och sedan, ja, var det dereference operatör, som du frågar, går och hämtar information vid en speciell adress. Så står det, med denna variabel här, behandla innehållet i en som en adress, gå till den adressen och dra ut, ladda in i processorn, ladda in i ett register de faktiska värdena eller innehållet som lever på denna adress. Några fler frågor? Det är bra frågor. Det är en hel del ny terminologi också. Det är också typ av funky, se & och * på olika platser. Okej. Så tillbaka till problemet 33, fil I / O. Detta var en av de problem som jag tror ett par saker hände. Ett, det är en ganska ny tråd. Det presenterades ganska snart innan testet, och då tror jag att det var ungefär som en av de ord problem i matematik där de ger dig en hel del information, men du faktiskt inte sluta med att använda massor av det. Den första delen av detta problem beskriver vad en CSV-fil är. Nu en CSV-fil, enligt beskrivningen, är en kommaseparerad värden fil. Anledningen till att dessa är på alla intressanta, och anledningen du någonsin använda dem, är därför, hur många av er någonsin använt saker som Excel? Figur flesta av er har förmodligen kommer eller använda någon gång i ditt liv. Du kommer att använda något som Excel. För att få de uppgifter ur ett Excel-kalkylblad eller göra någon form av behandling med det, om du ville skriva en C-program eller Python program, Java-program, att ta itu med de data du har lagrat i det, en av de vanligaste sätten att få ut det i en CSV-fil. Och du kan öppna upp Excel och när du går till "Spara som" dialog, du kan få ut en verklig CSV-fil. Praktiskt att veta hur man ska hantera dessa saker. Så det fungerar är att det är liknande - jag menar, det imitera huvudsak ett kalkylblad, där, som vi ser här, i mycket längst till vänster stycke, Vi har alla efternamn. Så vi har Malan, sedan Hardison och sedan Bowden, MacWilliam och sedan Chan. Alla efternamn. Och sedan ett komma separerar de sista namnen från de första namnen. David, Nate, Rob, Tommy och Zamyla. Jag blandar alltid Robby och Tom. Och, slutligen, är den tredje kolumnen e-postadresser. När du förstår det, är resten av programmet ganska enkelt att genomföra. Vad vi har gjort för att efterlikna samma struktur i vårt C-program är att vi har använt en struktur. Vi börjar spela med dem lite mer också. Vi såg dem för första lite i problembild 3, när vi hade att göra med ordböcker. Men denna personal struct lagrar ett efternamn, ett förnamn och e-post. Precis som vår CSV-fil var förvaring. Så detta är bara konvertera från ett format till ett annat. Vi måste omvandla, i detta fall, en personal struct till en linje, en kommaseparerad linje, bara så där. Verkar det vettigt? Ni har alla tagit testet, så jag antar att du har åtminstone haft tid att tänka på detta. I hyra funktionen frågar problemet för oss att ta i - we'll zooma in på detta lite - ta i en personalstruktur, en personal struct, med namn s och bifoga dess innehåll till vår staff.csv fil. Det visar sig att det är ganska enkelt att använda. Vi kommer slags leka med dessa funktioner lite mer idag. Men i detta fall är fprintf funktion verkligen nyckeln. Så med fprintf kan vi skriva ut, precis som ni har använt printf hela denna term. Du kan printf en linje till en fil. Så istället för att bara göra vanliga printf samtalet där du ger den formatsträngen och sedan ersätta alla variabler med följande argument, med fprintf är din allra första argument istället den fil du vill skriva till. Om vi ​​skulle titta på detta i apparaten, till exempel, man fprintf, Vi kan se skillnaden mellan printf och fprintf. Jag zooma in här lite. Så med printf ger vi det en formatsträng, och sedan de efterföljande argumenten är alla variabler för utbyte eller ersättning i vår formatsträng. Medan med fprintf är det första argumentet verkligen den här filen * som kallas en ström. Flytta tillbaka hit till vår hyra, Vi har redan fått vår fil * ström öppnas för oss. Det är vad denna första raden gör, det öppnar staff.csv filen, öppnar den i append mode, och allt som är kvar för oss att göra är skriva personalstrukturen till filen. Och ska vi se, jag vill använda iPad? Jag kommer att använda iPad. Vi har ogiltig - låt oss sätta detta på bordet så jag kan skriva lite bättre - ogiltig hyra och det tar i ett argument, en personal struktur som kallas talet. Fick våra hängslen, vi har vår fil * heter filen, Vi har vår fopen linje som ges till oss, och jag ska bara skriva det som prickar eftersom det är redan i pedia. Och sedan på vår nästa rad, kommer vi att ringa ett samtal till fprintf och vi kommer att passera i den fil som vi vill skriva ut till, och sedan vår formatsträng, som - Jag ska låta er berätta hur det ser ut. Och du, Stella? Vet du vad den första delen av formatsträngen ser ut? [Stella] Jag är inte säker. >> Fråga gärna Jimmy. Vet du, Jimmy? [Jimmy] Skulle det vara bara förra? Jag vet inte. Jag är inte helt säker. >> Okej. Vad sägs om, fick någon få denna rätt på tentan? Nej Okej. Det visar sig att här allt vi behöver göra är att vi vill att varje del av vår personal struktur som ska skrivas ut som en sträng i vår fil. Vi använder bara strängen ersättning tecknet tre olika tillfällen eftersom vi har ett efternamn följt av kommatecken, då ett förnamn följt av ett kommatecken, och sedan slutligen den e-postadress som följs - vilket inte är montering på min skärm - men det följs av en radmatningstecken. Så jag kommer att skriva det bara där. Och sedan efter vår formatsträng, Vi har bara de substitutioner som vi åt med punktnotation som vi såg i problembild 3. Vi kan använda s.last, s.first och s.email att ersätta i de tre värden i vår formatsträng. Så hur gick det? Vettigt? Ja? Nej? Möjligen? Okej. Den sista sak som vi gör när vi har skrivit och efter att vi har öppnat vår fil: när vi har öppnat en fil, måste vi alltid komma ihåg att stänga den. Eftersom vi annars kommer att sluta läcka minnet, med upp fil deskriptorer. Så för att stänga den, vilket fungerar använder vi? Daniel? [Daniel] fclose? >> Fclose, exakt. Så den sista delen av detta problem var att ordentligt stänga filen, med hjälp av fclose funktionen som ser ut precis så. Inte för galet. Cool. Så det är problem 33 på testet. Vi kommer att ha definitivt mer I / O-kommer upp. Vi ska göra lite mer i föreläsning idag, eller i avsnitt idag, eftersom det är vad som kommer att bilda huvuddelen av denna kommande pset. Låt oss gå vidare från testet på denna punkt. Ja? [Charlotte]] Varför fclose (fil) i stället för fclose (staff.csv)? >> Ah. Eftersom det visar sig att - så frågan, som är en stor en, varför, när vi skriver fclose, vi skriver fclose (fil) stjärna variabel i motsats till filnamnet, staff.csv? Är det korrekt? Ja. Så låt oss ta en titt. Om jag byter tillbaka till min laptop, och låt oss titta på fclose funktionen. Så fclose funktionen stängs en ström och det tar i pekaren till bäcken som vi vill stänga, i motsats till den verkliga filnamnet som vi vill stänga. Och detta beror bakom kulisserna när du ringer till fopen, när du öppnar en fil, du fördela faktiskt minne för att lagra information om filen. Så du har filer pekare som har information om filen, som det är öppet, dess storlek, där du är just nu i filen, så att du kan göra läsa och skriva samtal till den platsen i filen. Du hamnar stänga pekaren istället för att stänga filnamnet. Ja? [Daniel] Så för att använda hyra, skulle du säga - hur går det få användaren ingång? Agerar som fprintf GetString i den meningen att det bara väntar på användarens inmatning och ber dig att skriva detta - eller vänta på dig att skriva dessa tre saker i? Eller behöver du använda något för att genomföra hyra? >> Ja. Så vi är inte - frågan var, hur vi får användarinmatning för att genomföra hyra? Och vad vi har här är den som ringer i hyra, antogs i denna personal struct med alla data som lagras i struct redan. Så fprintf kan bara skriva dessa data direkt till filen. Det finns ingen väntar på indata. Användaren har redan gett bidrag genom att korrekt sätta den i denna personal struct. Och saker, naturligtvis, skulle gå sönder om någon av dessa pekare var noll, så vi rullar tillbaka hit och vi ser på vår struct. Vi har sträng sist, sträng först sträng e-post. Vi vet nu att alla dessa verkligen, under huven, är char * variabler. Det kan eller inte kan peka till null. De kan peka på minne på högen, kanske minne på stacken. Vi vet inte riktigt, men om någon av dessa tips är noll, eller ogiltigt, att det definitivt kommer att krascha vår hyra funktion. Det var något som var lite utanför ramen för provet. Vi är inte oroande om det. Jättebra. Okej. Så gå vidare från testet. Låt oss avsluta den här killen, och vi kommer att titta på pset 4. Så om ni ser på pset spec, när du kan komma åt den, cs50.net/quizzes, Vi kommer att gå igenom några av avsnittet problem idag. Jag rulla ner - del frågor börjar på den tredje sidan av pset spec. Och den första delen ber dig att gå och titta på kort på omdirigera och rör. Som var typ av en cool kort, visar några nya, coola tricks kommandorad som du kan använda. Och sedan har vi ett par frågor till er också. Denna första fråga om strömmar, som printf skriver som standard, vi typ av berört bara lite för en stund sedan. Det fprintf att vi bara diskuterade tar i en fil * ström som argument. fclose tar i en fil * ström också, och avkastningen värdet av fopen ger en ström fil * också. Anledningen till att vi inte har sett dem förut när vi har behandlat printf beror printf har en standard ström. Och standard strömmen som den skriver hittar du reda på kort. Så definitivt ta en titt på det. I dagens avsnitt ska vi prata lite om GDB, eftersom mer bekant du är med det, desto mer praktik får du med det, desto bättre kan du vara att faktiskt jaga buggar i din egen kod. Detta påskyndar processen med felsökning upp enormt. Så genom att använda printf, varje gång du gör det du måste kompilera om koden, du måste köra det igen, ibland måste man flytta printf samtalet runt, kommentera ut kod, det tar bara ett tag. Vårt mål är att försöka övertyga dig om att med GDB kan du i huvudsak printf någonting när som helst i din kod och du behöver aldrig kompilera det. Du behöver aldrig starta och hålla gissa var man printf nästa. Det första du bör göra är att kopiera denna linje och få avsnittsmarkeringen bort av webben. Jag kopierar denna kodrad som säger, "wget ​​http://cdn.cs50.net". Jag ska kopiera den. Jag ska gå över till min apparat, zooma ut så att du kan se vad jag gör, klistra in den där, och när jag slog in, denna wget kommandot bokstavligen är ett webbaserat få. Det kommer att dra ner den här filen bort av Internet, och kommer rädda det aktuell katalog. Nu om jag lista min nuvarande katalogen kan du se att jag har denna section5.zip fil rätt där. Det sättet att hantera den där killen är att packa upp den, som du kan göra på kommandoraden, precis som här. Section5.zip. Det kommer packa upp den, skapa en mapp för mig, blåsa hela innehållet, lägg dem där. Så nu kan jag gå in i min avsnitt 5 katalog med kommandot cd. Rensa skärmen med tydlig. Så rensa skärmen. Nu har jag en trevlig ren terminal att ta itu med. Nu om jag lista alla filer som jag ser i den här katalogen, Ni ser att jag har fyra filer: buggy1, buggy2, buggy3 och buggy4. Jag har också fått motsvarande. C. filer. Vi kommer inte att titta på. C-filer för nu. Istället kommer vi att använda dem när vi öppnar upp GDB. Vi har hållit dem runt så att vi har tillgång till den aktuella källkoden när vi använder GDB, men målet för denna del av avsnittet är att mixtra runt med GDB och se hur vi kan använda den för att räkna ut vad som händer fel med var och en av dessa fyra buggy program. Så vi ska bara runt i rummet verkligen snabbt, och jag kommer att be någon att köra ett av de buggiga program, och sedan går vi som en grupp genom GDB, och vi får se vad vi kan göra för att åtgärda dessa program, eller åtminstone identifiera vad som händer fel i varje av dem. Låt oss börja här borta med Daniel. Kommer du köra buggy1? Låt oss se vad som händer. [Daniel] Det säger att det finns ett program fel. >> Ja. Exakt. Så om jag kör buggy1 får jag en seg fel. Vid denna punkt, jag kunde gå och öppna buggy1.c, försöka lista ut vad som går fel, men en av de mest motbjudande saker om detta segment fel fel är att det inte berätta om vad raden av programmets sakerna faktiskt gick fel och bröt. Du sorts måste titta på koden och räkna ut med gissning och kontrollera eller printf att se vad som går fel. En av de coolaste sakerna med GDB är att det är riktigt, riktigt enkelt att räkna ut den linje där ditt program kraschar. Det är helt värt det att använda den, även om det bara för att. Så för att starta upp GDB skriver jag GDB, och då jag ge den sökvägen till den körbara som jag vill köra. Här jag skriver gdb ./buggy1. Hit Enter. Ger mig all denna information om copyright, och här nere ser du denna linje som säger, "Läsa symboler från / home / jharvard/section5/buggy1. " Och om allt går bra, kommer du att se det skriva ut ett budskap som ser ut så här. Det ska läsa symboler, det ska säga "Jag läser symboler från körbar fil" och så kommer det att ha detta "gjort" budskap här. Om du ser någon annan variant av detta, eller om du ser att det inte kunde hitta de symboler eller något liknande, vad det betyder är att du bara inte har sammanställt din körbara ordentligt. När vi sammanställer program för användning med GDB måste vi använda den särskilda g flagga, och det är gjort som standard om du kompilerar dina program, bara genom att skriva att eller göra buggy eller göra återhämta någon av dessa. Men om du kompilera manuellt med klang, då måste du gå in och bland annat att-g flaggan. Vid denna punkt, nu när vi har vår GDB prompten det är ganska enkelt att köra programmet. Vi kan antingen skriva springa, eller så kan vi bara skriva r.. De flesta GDB kommandon kan förkortas. Vanligtvis bara en eller ett brev par, vilket är ganska trevligt. Så Saad, om du skriver r och trycker Enter, vad händer? [Saad] Jag har SIGSEGV, segmenteringsfel och sedan allt detta fikonspråk. >> Ja. Som vi ser på skärmen just nu, och som Saad sade, När vi skriver körning eller r och tryck Enter, får vi fortfarande samma seg fel. Så använder GDB löser inte vårt problem. Men det ger oss lite fikonspråk, och det visar sig att denna rappakalja faktiskt berättar om det händer. Att tolka detta lite, är detta första biten funktionen där allt händer fel. Det är detta __ strcmp_sse4_2, och det säger oss att det som händer i den här filen kallas sysdeps/i386, allt detta igen, typ av en enda röra - men linje 254. Det är typ av svårt att tolka. Vanligtvis när du ser sånt här, det betyder att det är seg förkastningar i en av systemets bibliotek. Så något att göra med strcmp. Ni har sett strcmp förut. Inte alltför galet, men betyder det att strcmp är trasig eller att det finns ett problem med strcmp? Vad tror du, Alexander? [Alexander] Är det - är 254 linjen? Och - inte binära, men det är inte deras tak, och sedan finns det ett annat språk för varje funktion. Är det 254 i den funktionen, eller -? >> Det är linje 254. Det ser ut som detta. Akt, så det är assemblerkod förmodligen. Men jag antar att det mer angelägen sak är, att vi har fått en seg fel, och det ser ut som det kommer från strcmp funktionen, innebär detta innebära, då är det strcmp sönder? Det bör inte, förhoppningsvis. Så bara för att du har ett segmenteringsfel i en av systemets funktioner innebär oftast att du bara inte har kallat det på rätt sätt. Det snabbaste du kan göra för att räkna ut vad som egentligen händer När du ser något galet så här, när du ser en seg fel, särskilt om du har ett program som är med mer än bara stora, är att använda en bakåtspårning. Jag förkorta bakåtspårning genom att skriva BT i motsats till den fullständiga bakåtspårning ordet. Men Charlotte, vad som händer när du skriver BT och tryck på Retur? [Charlotte] Det visar mig två linjer, linje 0 och linje 1. >> Ja. Så linje 0 och linje 1. Dessa är de verkliga stack ramar som var närvarande i spel när programmet kraschade. Med utgångspunkt från den översta ramen, ram 0 och gå till nedersta, som är ramen 1. Vår översta ramen är det strcmp ramen. Du kan tänka på detta som liknar det problemet vi bara gör på frågesport med pekare, där vi hade byta stack ram ovanpå viktigaste stack ram, och vi hade de variabler som swap använde ovanpå de variabler som huvudsakliga använde. Här vår krasch hände i vår strcmp funktion, som kallades av våra huvudsakliga funktion, och bakåtspårning ger oss inte bara de funktioner som saker misslyckades, men det är också berättar där allt kallades från. Så om jag rullar över lite mer åt höger, kan vi se att ja, vi var på rad 254 i detta strcmp-sse4.s fil. Men samtalet gjordes vid buggy1.c, linje 6. Så det betyder att vi kan göra - är att vi kan bara gå checka ut och se vad som pågick på buggy1.c, linje 6. Återigen finns det ett par sätt att göra detta. En är att avsluta från GDB eller har din kod öppnas i ett nytt fönster och korsreferens. Att i och för sig är ganska praktiskt för nu om du är på kontorstid och du har en seg fel och din TF är undrar var allt sönder, Du kan bara säga, "Åh, rad 6. Jag vet inte vad som händer, men något om ledningen 6 orsakar mitt program för att bryta. " Det andra sättet att göra det är att du kan använda detta kommando kallas lista i GDB. Du kan också förkorta den med l. Så om vi slår jag, vad vi hit? Vi får en massa konstiga saker. Detta är den verkliga assemblerkod som är i strcmp_sse4_2. Detta ser typ av funky, och anledningen vi får detta beror just nu, GDB har oss i ram 0. Så fort vi ser på variabler, varje gång vi tittar på källkoden, vi tittar på källkoden som hänför sig till stacken ram vi nu i. Så för att få något meningsfullt, måste vi flytta till en skorsten ram som är mer förnuftigt. I detta fall skulle den största stacken ramen gör lite mer känsla, eftersom det var faktiskt den kod som vi skrev. Inte strcmp koden. Det sätt du kan flytta mellan ramarna, i detta fall, eftersom vi har två, vi har 0 och 1, du gör det med upp och ner-kommandon. Om jag flyttar upp en ram, nu är jag i huvudsak stacken ram. Jag kan flytta ner för att gå tillbaka till där jag var, gå upp igen, gå ner igen, och gå upp igen. Om du någonsin gör ditt program i GDB får du en krasch, får du bakåtspårning, och du ser att det är i någon fil som du inte vet vad som händer. Du försöker lista ser koden inte bekant för dig, ta en titt på dina bilder och räkna ut var du befinner dig. Du är förmodligen i fel stacken ram. Eller åtminstone du är i en skorsten ram som inte är en som du verkligen kan felsöka. Nu när vi är i rätt stackram, vi är i huvud, Nu kan vi använda kommandot list att lista ut vad linjen var. Och du kan se det, det skrivs det för oss här. Men vi kan slå lista alla lika, och listan ger oss denna fina utskrift av den faktiska källkod som händer här. I synnerhet, kan vi titta på ledningen 6. Vi kan se vad som händer här. Och det ser ut som vi gör en sträng jämförelse mellan strängen "CS50 rocks" och argv [1]. Något om detta krascha. Så Missy, har du några tankar om vad som kan händer här? [Missy] Jag vet inte varför det är kraschar. >> Du vet inte varför det är kraschar? Jimmy, några tankar? [Jimmy] Jag är inte helt säker, men förra gången vi använde sträng jämföra, eller strcmp hade vi som tre olika fall under den. Vi hade inte en ==, tror jag inte, mitt i den första raden. Istället separerades i tre, och en var == 0, en var <0, tror jag, och en var> 0. Så kanske något sådant? >> Ja. Så det finns denna fråga av gör vi jämförelsen korrekt? Stella? Några tankar? [Stella] Jag är inte säker. >> Osäker. Daniel? Tankar? Okej. Det visar sig vad som händer här är när vi körde programmet och vi fick seg fel, när du körde programmet för första gången, Daniel, gav du det någon kommandoradsargument? [Daniel] Nej >> Nej I så fall, vad är värdet av argv [1]? >> Det finns inget värde. >> Höger. Tja, det finns inget rätt sträng värde. Men det finns något värde. Vad är det värde som får lagras där? >> En sopor värde? >> Det är antingen ett skräp värde eller, i detta fall, änden av argv matrisen alltid avslutas med null. Så vad som egentligen blev lagrats i det är null. Det andra sättet att lösa detta, snarare än att tänka igenom det, är att försöka skriva ut. Det är där jag sa att använda GDB är stor, eftersom du kan skriva ut alla variabler, alla värden som du vill använda denna behändiga-dandy p kommandot. Så om jag skriver p och då jag skriver värdet på en variabel eller namnet på en variabel, säga argc ser jag att argc är 1. Om jag vill skriva ut argv [0], kan jag göra det bara så där. Och som vi såg, argv [0] är alltid namnet på ditt program, alltid namnet på den körbara. Här ser det har fått den fullständiga sökvägen. Jag kan också skriva ut argv [1] och se vad som händer. Här fick vi den här typen av mystiska värde. Vi fick denna 0x0. Tänk i början av perioden när vi pratade om hexadecimala tal? Eller den lilla frågan i slutet av pset 0 om hur man representera 50 i hex! Det sätt vi skriver hexadecimala tal i CS, bara inte förvirra oss med decimaltal, är vi alltid prefix dem 0x. Så denna prefixet 0x alltid bara betyder tolka följande nummer som ett hexadecimalt tal, inte som en sträng, inte som ett decimaltal, inte som ett binärt tal. Eftersom antalet 5-0 är ett giltigt nummer i hexadecimal. Och det är ett nummer i decimal, 50. Så detta är bara hur vi disambiguera. Så 0x0 betyder hexadecimalt 0, vilket också decimal 0, binära 0. Det är bara värdet 0. Det visar sig att detta är vad null är, faktiskt, i minnet. Null är bara 0. Här, elementet lagrades vid argv [1] är null. Så vi försöker jämföra vår "CS50 rocks" sträng till en tom sträng. Så dereferencing null, försöker komma åt saker på noll, de är oftast kommer att orsaka någon form av segmentering fel eller andra dåliga saker att hända. Och det visar sig att strcmp inte kontrollera om du har passerat i ett värde som är noll. Snarare det bara går framåt, försöker göra sin grej, och om det seg fel, seg det fel, och det är ditt problem. Du måste gå fixa det. Riktigt snabbt, hur kan vi lösa detta problem? Charlotte? [Charlotte] Du kan kontrollera med om. Så om argv [1] är null == 0, sedan tillbaka 1, eller något [obegripligt]. >> Ja. Så det är en bra sätt att göra det, eftersom vi kan kontrollera, det värde vi är på väg att passera in strcmp, argv [1], är null den? Om det är noll, då kan vi säga okej, avbryta. Ett vanligare sätt att göra detta är att använda argc värdet. Du kan se här i början av huvud, Vi utelämnat som första test som vi vanligtvis gör när vi använder kommandoradsargument, vilket är att testa huruvida våra argc värde är vad vi förväntar oss. I det här fallet, vi förväntar åtminstone två argument, namnet på programmet plus en annan. Eftersom vi är på väg att använda det andra argumentet här. Så att ha någon form av test i förväg, innan vår strcmp samtal att tester eller inte argv är minst 2, skulle också göra samma sak. Vi kan se om det fungerar genom att köra programmet igen. Du kan alltid starta om program inom GDB, vilket är riktigt nice. Du kan köra, och när du passerar på argument till ditt program, du passerar dem i när du ringer springa, inte när du startar upp GDB. På så sätt kan du hålla åberopar ditt program med olika argument varje gång. Så kör, eller igen, kan jag typ R, och låt oss se vad som händer om vi skriver "Hej". Det kommer alltid att fråga dig om du vill starta det från början igen. Vanligtvis vill du starta det från början igen. Och på denna punkt, det startar den igen, skriver ut det program som vi kör, buggy1, med argumentet hej, och det skriver denna standard ut, det säger, "Du får ett D," ledsen ansikte. Men vi inte seg fel. Det sägs att processen avslutades normalt. Så det ser ganska bra ut. Inga fler seg fel, vi gjorde det tidigare, så det ser ut som det var verkligen seg fel bugg som vi fick. Tyvärr säger det oss att vi får ett D. Vi kan gå tillbaka och titta på koden och se vad som händer där att räkna ut vad var - varför det säger att vi fick ett D. Låt oss se, här var det sa printf att du fick ett D. Om vi ​​skriver lista, som du håller skriva lista, det håller iterera ner genom ditt program, så det kommer att visa dig de första raderna i ditt program. Sen ska visa dig de närmaste linjer, och nästa bit och nästa bit. Och det ska fortsätta att försöka gå ner. Och nu kommer vi till "radnummer 16 är utom räckhåll." Eftersom det har endast 15 rader. Om du kommer till denna punkt och du undrar, "Vad ska jag göra?" Du kan använda hjälpen kommandot. Använd hjälp och sedan ge den namnet på en kommando. Och du ser GDB ger oss alla här typen av grejer. Det säger, "Utan argument listar tio fler linjer efter eller runt tidigare notering. Lista - listar de tio raderna innan - " Så låt oss försöka använda listan minus. Och som listar 10 rader tidigare, du kan leka med listan lite. Du kan göra listan, lista - Du kan ge ännu lista ett antal, som lista 8, och det kommer att lista de 10 linjerna runt linje 8. Och du kan se vad som händer här är att du har en enkel om annat. Om du skriver in CS50 stenar, skriver ut "Du får ett A." Annars skriver ut "Du får ett D." Bummer stad. Okej. Ja? [Daniel] Så när jag försökte göra CS50 stenar utan citattecken, det står "Du får ett D." Jag behövde citat för att få det att fungera, varför är det? >> Ja. Det visar sig att när - det är en annan rolig liten godbit - När du kör programmet, om vi kör det och vi skriver in CS50 stenar, precis som Daniel sa att han gjorde det, och du trycker på Retur, det står fortfarande vi få en D. Och frågan är, varför är det här? Och det visar sig att både vår terminal och GDB tolka dessa som två olika argument. För när det finns en plats, som är underförstådd i det första argumentet slutade, nästa argumentet är på väg att börja. Sättet att kombinera dem i två eller ledsen, till ett argument, är att använda citattecken. Så nu, om vi sätter det inom citationstecken och köra det igen, får vi ett A. Så bara för att sammanfatta, inga citat, CS50 och stenar tolkas som två olika argument. Med citat, det tolkas som ett argument helt och hållet. Vi kan se detta med en brytpunkt. Hittills har vi kört vårt program, och det varit igång tills antingen det seg fel eller träffar ett fel eller tills det har lämnat och allt har varit helt bra. Detta är inte nödvändigtvis det mest användbara sak, för ibland du har ett fel i ditt program, men det är inte orsakar ett segmenteringsfel. Det är inte orsakar ditt program för att stoppa eller nåt sånt. Det sättet att få GDB att pausa ditt program vid en viss punkt är att sätta en brytpunkt. Du kan antingen göra detta genom att ställa en brytpunkt på ett funktionsnamn eller så kan du sätta en brytpunkt på en viss kodrad. Jag gillar att ställa brytpunkter på funktionsnamn, eftersom - lätt att komma ihåg, och om du faktiskt gå in och ändra din källkod upp lite, då din brytpunkt kommer faktiskt bo på samma ställe i din kod. Medan om du använder radnummer och radnummer ändras eftersom du lägger till eller tar bort lite kod, då dina brytpunkter är alla helt skruvas upp. En av de vanligaste sakerna jag gör är sätta en brytpunkt på den huvudsakliga funktionen. Ofta jag starta upp GDB, jag typ b viktigaste, tryck Enter, och det kommer att sätta en brytpunkt på den huvudsakliga funktion som bara säger "pausa programmet så fort du börjar köra," och på det sättet, när jag kör mitt program med, säg, CS50 stenar som två argument och trycker Enter, blir det till huvudfunktionen och den stannar precis vid den allra första raden, precis innan den utvärderar strcmp funktionen. Eftersom jag pausat, nu kan jag börja mucking runt och se vad som händer med alla de olika variabler som skickas till mitt program. Här kan jag skriva ut argc och se vad som händer. Se till att argc är 3, eftersom det har fått 3 olika värden i den. Det har fått namnet på programmet, den har det första argumentet och det andra argumentet. Vi kan skriva dem ut genom att titta på argv [0], argv [1], och argv [2]. Så nu kan du också se varför denna strcmp samtal kommer att misslyckas, eftersom du ser att det gjorde dela upp CS50 och klipporna i två olika argument. Vid denna punkt, när du har träffat en brytpunkt, kan du fortsätta att gå igenom ditt program rad för rad, i motsats till start programmet igen. Så om du inte vill starta programmet igen och bara fortsätta vidare härifrån, Du kan använda Fortsätt kommandot och fortsätta att köra programmet till slutet. Precis som det gjorde här. Men om jag startar programmet CS50 stenar, träffar det min brytpunkten igen, och den här gången, om jag inte vill gå hela vägen genom resten av programmet, Jag kan använda nästa kommando, som jag också förkorta med n. Och detta kommer gå igenom programmet rad för rad. Så du kan titta på när saker och ting utför, som variabler förändras, eftersom saker och ting få uppdaterad. Vilket är ganska trevligt. Den andra häftiga är snarare än att upprepa samma kommando om och om och om igen, om du bara trycka Enter - så här ser du att jag inte har skrivit in något - Om jag bara trycka Enter, kommer det att upprepa föregående kommando, eller den tidigare GDB kommando som jag satte bara in Jag kan hålla trycka Enter och det ska hålla stegar genom min kod rad för rad. Jag skulle vilja uppmuntra er att gå kolla in de andra buggig program också. Vi har inte tid att gå igenom dem alla i dag i snitt. Källkoden finns där, så att du kan typ av se vad som händer bakom kulisserna om du verkligen fastnar, men åtminstone, bara öva startar upp GDB, kör programmet tills den går sönder på dig, få bakåtspårning, räkna ut vilken funktion kraschen var, vilken linje det var på, skriva ut några variabelvärden, bara så du får en känsla för det, eftersom det verkligen kommer att hjälpa dig att gå framåt. Vid denna punkt, kommer vi att sluta av GDB, som du gör med sluta eller bara q.. Om ditt program är i mitten av fortfarande igång, och det har inte lämnat, det kommer alltid att be dig, "Är du säker på att du verkligen vill avsluta?" Du kan bara trycka ja. Nu ska vi titta på nästa problem vi har, vilket är katten programmet. Om du tittar på kort på omdirigering och rör, ser du att Tommy använder programmet som skriver i princip samtliga produkter en fil på skärmen. Så om jag kör katt, är detta faktiskt en inbyggd program till apparaten, och om du har Mac kan du göra det på din Mac även om du öppnar terminalen. Och vi - katt, låt oss säga, cp.c och tryck på Retur. Vad detta gjorde, om vi bläddra upp lite och se var vi körde linjen, eller där vi körde katten kommandot, det bokstavligen bara skrivas ut innehållet i cp.c till vår skärm. Vi kan köra den igen och du kan sätta i flera filer samtidigt. Så du kan göra katten cp.c, och sedan kan vi också sammanfoga den Cat.C filen, vilket är det program som vi är på väg att skriva, och det kommer ut båda filerna tillbaka till tillbaka till våra skärmen. Så om vi bläddra upp lite, ser vi att när vi körde denna katt cp.c, Cat.C, Först skrivs ut cp filen och under den, tryckt ut den Cat.C filen ända ner hit. Vi kommer att använda detta för att bara få våra fötter våt. Leka med enkel utskrift till terminalen, se hur det fungerar. Om ni öppnar upp med gedit Cat.C, tryck Enter, Du kan se programmet som vi är på väg att skriva. Vi har tagit med denna fina panna plattan, så att vi inte behöver lägga tid på att skriva allt ut. Vi kontrollerar också många argument som skickas in Vi skriver ut en fin användning meddelande. Detta är det slags saker att igen, som vi har talat om, det är nästan som muskel minne. Kom bara ihåg att hålla gör samma sorts saker och alltid skriva ut någon form av hjälp budskap så att människor vet hur du kör ditt program. Med katt, det är ganska enkelt, vi ska bara gå igenom alla de olika argumenten som skickas till vårt program, och vi kommer att skriva ut deras innehåll ut till skärmen en i taget. För att skriva ut filer ut till skärmen, vi kommer att göra något liknande vad vi gjorde i slutet av testet. I slutet av testet, som hyr programmet hade vi att öppna upp en fil, och sedan var vi tvungna att skriva ut på den. I det här fallet kommer vi att öppna upp en fil, och vi kommer att läsa från den i stället. Sen ska vi ut, i stället för till en fil kommer vi att skriva på skärmen. Så utskrift till skärmen du har alla gjort innan med printf. Så det är inte alltför galen. Men läser en fil är lite konstigt. Vi kommer att gå igenom det en liten bit i taget. Om ni går tillbaka till det sista problemet på frågesport, problemet 33, den första raden som vi ska göra här, öppna filen, är mycket likt det vi gjorde där. Så Stella, vad den raden ser ut, när vi öppnar en fil? [Stella] Capital FIL *, fil - >> Okej. >> - Är lika med fopen. >> Japp. Som i detta fall är? Det är i kommentaren. >> Det är i kommentar? argv [i] och r? >> Exakt. Rätt på. Så Stella är helt rätt. Detta är vad raden ser ut. Vi kommer att få en variabel filflödet, förvara den i en fil *, så alla mössor, FIL, *, och namnet på denna variabel är fil. Vi kan kalla det vad vi vill. Vi kan kalla det first_file eller file_i, vad vi vill. Och sedan namnet på filen antogs i på kommandoraden i programmet. Så det är lagrat i argv [i,] och sedan ska vi öppna filen i skrivskyddat läge. Nu när vi har öppnat filen är vad det som vi alltid måste komma ihåg att göra när vi har öppnat en fil? Stänga det. Så Missy, hur vi stänger en fil? [Missy] fclose (fil) >> fclose (fil). Exakt. Jättebra. Okej. Om vi ​​tittar på det här att göra kommentar här, står det "Open argv [i] och skriv ut dess innehåll till stdout." Standard ut är ett konstigt namn. Stdout är bara vårt sätt att säga Vi vill skriva ut den till terminalen, vi vill skriva ut det till standard ut strömmen. Vi kan faktiskt bli av med denna kommentar just här. Jag ska kopiera den och klistra in den eftersom det är vad vi gjorde. Vid denna punkt, nu måste vi läsa filen bit för bit. Vi har diskuterat ett par olika sätt att läsa filer. Vilka är dina favoriter hittills? Vilka vägar har ni sett eller minns du, att läsa filer? [Daniel] fread? >> Fread? Så fread är en. Jimmy, vet du några andra? [Jimmy] Nej >> Okej. Nix. Charlotte? Alexander? Några andra? Okej. Så de andra är fgetc är en som vi kommer att använda mycket. Det finns också fscanf, ni ser ett mönster här? De börjar alla med f.. Något att göra med en fil. Det finns fread, fgetc, fscanf. Dessa är alla läser funktioner. För att skriva att vi har fwrite har vi fputc istället för fgetc. Vi har även gillar fprintf vi såg på testet. Eftersom detta är ett problem som innebär att läsa från en fil, Vi kommer att använda en av dessa tre funktioner. Vi kommer inte att använda dessa funktioner här nere. Dessa funktioner är alla finns i standard I / O-bibliotek. Så om du tittar på toppen av detta program, kan du se att vi redan har tagit med huvudet filen för standard I / O-bibliotek. Om vi ​​vill ta reda på vilken vi vill använda, Vi kan alltid öppna upp man-sidor. Så vi kan skriva människan stdio och läs allt om stdio in-och utgångsfunktioner i C. Och vi kan redan se Åh, titta. Det nämna fgetc, det nämna fputc. Så du kan borra ner lite och titta på, säg, fgetc och titta på dess manualsida. Du kan se att det går tillsammans med en massa andra funktioner: fgetc, fgets, getc, getchar blir, ungetc och dess inmatning av tecken och strängar. Så detta är hur vi läser i tecken och strängar från filer från standard input, som är väsentligen från användaren. Och det är hur vi gör det i själva C. Så detta inte använder GetString och getchar funktioner som vi använde från CS50 biblioteket. Vi ska göra det här problemet på ett par olika sätt så att du kan se två olika sätt att göra det. Både fread funktion som Daniel nämnde och fgetc är bra sätt att göra det. Jag tror fgetc är lite lättare, eftersom den bara har, som ni ser, ett argument, filen * som vi försöker läsa karaktär från, och returvärdet är en int. Och detta är lite förvirrande, eller hur? Eftersom vi får en karaktär, så varför inte denna avkastning en röding? Ni har några idéer om varför detta inte kan returnera en char? [Missy svarar obegripligt] >> yeah. Så Missy är helt rätt. Om det är ASCII, då detta heltal kan mappas till en faktisk röding. Kan vara ett ASCII-tecken, och det är rätt. Det är precis vad som händer. Vi använder en int helt enkelt eftersom det har fler bitar. Det är större än en röding, vår tecken endast har 8 bitar, att 1 byte på våra 32-bitars maskiner. Och en int har alla 4 byte värde av rymden. Och det visar sig att det sätt fgetc fungerar, om vi bläddra ner i vår synopsis i man-sidan lite, rulla hela vägen ner. Det visar sig att de använder denna särskilda värde som kallas EOF. Det är en speciell konstant som returvärde för fgetc funktion när du träffar slutet av filen, eller om du får ett felmeddelande. Och det visar sig att göra dessa jämförelser med EOF korrekt, du vill ha det där lilla extra mängden information som du har i en int i motsats till användning av en CHAR-variabel. Även om fgetc effektivt få en karaktär från en fil, du vill komma ihåg att det är tillbaka något som är av typen int för dig. Som sagt, det är ganska lätt att använda. Det kommer att ge oss ett tecken, så allt vi behöver göra är att fortsätta be filen "Ge mig nästa tecken, ge mig nästa tecken, ge mig nästa tecken," tills vi kommer till slutet av filen. Och det kommer att dra in ett tecken i taget från vår fil, och då kan vi göra vad vi vill med den. Vi kan lagra det, kan vi lägga till en sträng, kan vi skriva ut den. Gör något av detta. Zoomning ut igen och gå tillbaka till vårt Cat.C program, Om vi ​​ska använda fgetc, Hur kan vi närma detta nästa kodrad? Vi kommer att använda - fread kommer att göra något lite annorlunda. Och den här gången kommer vi bara att använda fgetc att få ett tecken i taget. Att bearbeta en hel fil, vad kan vi göra? Hur många tecken är det i en fil? Det finns en hel del. Så vill du förmodligen att få en och sedan få en annan och få en annan och få en annan. Vilken typ av algoritm tror du att vi kanske måste använda här? Vilken typ av -? [Alexander] en for-loop? >> Exakt. Någon typ av slinga. En for-slinga är faktiskt bra, i det här fallet. Och som du sa, det låter som du vill ha en slinga över hela filen, få ett tecken åt gången. Några förslag på vad som kan se ut? [Alexander, obegripligt] >> Okej, berätta bara på engelska vad du försöker göra? [Alexander, obegripligt] Så i det här fallet, det låter som vi försöker bara slinga över hela filen. [Alexander] Så jag > Storleken på -? Jag antar att storleken på filen, eller hur? Storleken - we'll skriva precis det så här. Storlek på filen för närvarande, i + +. Så det visar sig att det sätt du gör detta med fgetc, och detta är nytt, är att det finns inget enkelt sätt att bara få storleken på en fil med denna "sizeof" typ av konstruktion som du har sett förut. När vi använder det fgetc funktion, vi införa någon form av nya, funky syntax detta för slinga, där i stället för att använda bara en grundläggande räknare att gå tecken för tecken, kommer vi att dra ett tecken i taget, ett tecken i taget, och hur vi vet att vi är i slutet inte när vi har räknat ett visst antal tecken, men när karaktären vi drar ut är att särskilda filslut karaktär. Så vi kan göra detta genom - Jag kallar detta lm, och vi kommer att initiera den med vår första samtalet för att få det första tecknet från filen. Så denna del här, detta kommer att få en karaktär ur filen och lagra det i variabeln lm. Vi kommer att fortsätta göra detta tills vi kommer till slutet av filen, vilket vi gör genom att testa för tecken som inte är lika med den där speciella EOF karaktär. Och sedan istället för att göra ch + +, vilket skulle bara öka värdet, så om vi läser ett A ur fil, en huvudstad A, säg, ch + + skulle ge oss b, och vi skulle få c och sedan d.. Det är uppenbarligen inte vad vi vill. Vad vi vill ha här i denna sista bit vi vill få nästa tecken från filen. Så hur kan vi få nästa tecken från filen? Hur får vi det första tecknet från filen? [Student] fgetfile? >> Fgetc, eller ledsen, du helt rätt. Jag felstavade det där. Så ja. Här i stället för att göra ch + +, Vi ska bara ringa fgetc (fil) igen och lagra resultatet i vår samma lm variabel. [Student fråga, obegripligt] >> Det är där dessa fil * killar är speciell. Det sätt de arbetar är de - när du öppnar - när du först göra det fopen samtal, FILE * fungerar effektivt som en pekare till början av filen. Och sedan varje gång du ringer fgetc, flyttar den en karaktär genom filen. Så när du kallar detta, du inkrementera filen pekaren ett tecken. Och när du fgetc igen, du flytta den en annan karaktär och en annan karaktär och en annan karaktär och ett annat tecken. [Student fråga, obegripligt] >> Och that - ja. Det är typ av denna magiska under huven. Du håller bara uppräkning igenom. Vid det här laget, du kan faktiskt arbeta med ett tecken. Så hur kan vi skriva ut det här till skärmen, nu? Vi kan använda samma printf sak som vi använt tidigare. Att vi har använt hela terminen. Vi kan kalla printf, och vi kan passera i tecknet bara sådär. Ett annat sätt att göra det är snarare än att använda printf och med att göra detta formatsträng, Vi kan också använda någon av de andra funktionerna. Vi kan använda fputc, som skriver ett tecken till skärmen, förutom om vi tittar på fputc - låt mig zooma ut lite. Vi ser vad som är trevligt är det tar i det tecken som vi läser med hjälp av fgetc, men då måste vi ge det en ström för att skriva ut på. Vi kan också använda putchar funktionen, som kommer att sätta direkt till standard ut. Så det finns en hel del olika alternativ som vi kan använda för utskrift. De är alla i standard I / O-bibliotek. När du vill skriva ut - så printf, som standard, kommer att skriva ut den särskilda standarden ut ström, nämligen att stdout. Så vi kan bara hänvisa till det som typ av denna magiska värde, stdout här. Oops. Sätt semikolon utanför. Detta är en mycket ny, funky information här. Mycket av detta är mycket idiomatiskt, i den meningen att detta är kod som skrivs på detta sätt bara för att det är ren att läsa, lätt att läsa. Det finns många olika sätt att göra det, många olika funktioner som du kan använda, men vi tenderar att bara följa samma mönster om och om igen. Så bli inte förvånad om du ser koden ut så här kommer upp igen och igen. Okej. Vid denna punkt måste vi bryta för dagen. Tack för att du kom. Tack för att titta om du är online. Och vi ses nästa vecka. [CS50.TV]