[Powered by Google Translate] [Sectie 5 - Meer Comfortabele] [Rob Bowden - Harvard University] [Dit is CS50. - CS50.TV] Zoals ik al zei in mijn e-mail, zijn er een heleboel dingen die je kunt gebruiken anders dan het apparaat eigenlijk het probleem sets. Wij raden u het in het apparaat, alleen maar omdat dan kunnen we makkelijker helpen en we weten hoe alles gaat werken. Maar als een voorbeeld van waar je dingen kunt doen als, zeg, hebt u geen toegang om een ​​apparaat of u wilt werken in het Science Center kelder - die in feite hebben ze het apparaat ook - als je wilt overal werken. Een voorbeeld is heb je gezien / gehoord van SSH? SSH is eigenlijk net als verbinding te maken met iets. Eigenlijk, nu ben ik SSHed in het apparaat. Ik heb nooit rechtstreeks in het apparaat. Hier is het apparaat, en als je naar beneden kijkt hier zie je dit IP-adres. Ik heb nooit werken in het apparaat zelf; Ik heb altijd meer dan komen tot een iTerm2 venster / terminal-venster. U kunt SSH naar dat IP-adres, ssh jharvard@192.168.129.128. Ik herinner me dat nummer heel gemakkelijk, want het is zo'n mooi patroon. Maar dat zal mij vragen om mijn wachtwoord, en nu ben ik in het apparaat. Kortom, op dit punt, als het openen van een terminal binnenkant van het apparaat zelf, deze interface, maar u zou gebruiken, is precies hetzelfde als de interface die ik gebruik hier maar nu ben je SSHed. Je hoeft niet te SSH aan het apparaat. Een voorbeeld van een andere plaats kon je SSH is dat ik ben er vrij zeker van je standaard - Oh. Groter. Al moet je standaard FAS accounts op de FAS-servers. Voor mij, ik zou SSH naar rbowden@nice.fas.harvard.edu. Het zal u dat de eerste keer te vragen, en je zegt ja. Mijn wachtwoord is gewoon mijn FAS wachtwoord. En nu, ik ben SSHed naar de mooie servers, en ik kan wat ik wil hier doen. Veel van klassen die u zou kunnen uitvoeren, bijvoorbeeld 124, zullen moeten je spullen uploaden naar hier om daadwerkelijk in te dienen uw probleem sets. Maar zeggen dat je geen toegang heeft tot uw apparaat te hebben. Dan kunt u dingen doen, zoals hier zal het zeggen - Dit is gewoon onze rubriek vragen. Het zal u vragen om dit te doen in het apparaat. In plaats daarvan zal ik gewoon doen op de server. Ik ga uit te pakken dat. Het probleem zal zijn dat je gewend bent aan het gebruik van iets als gedit of wat binnenkant van het apparaat. Je gaat niet naar dat hebben op de FAS-server. Het is allemaal gewoon aan deze tekstuele interface. Dus je zou kunnen een van beide, probeer dan een teksteditor die ze hebben leren. Ze hebben Nano. Nano is meestal vrij eenvoudig te gebruiken. U kunt gebruik maken van uw pijlen en typ normaal. Dus dat is niet moeilijk. Als u wilt krijgen echt aantrekkelijk kunt u gebruik maken Emacs, die waarschijnlijk had ik niet moeten geopend, omdat ik weet niet eens hoe je Emacs te sluiten. Controle X, Control-C? Ja. Of u kunt gebruik maken van Vim, dat is wat ik gebruik. En dus die zijn uw opties. Als u niet wilt dat te doen, kunt u ook, als je kijkt naar manual.cs50.net-- Oh. Op een pc kunt u met behulp van SSH PuTTY, die je gaat te hebben om afzonderlijk te downloaden. Op een Mac kun je gewoon standaard gebruik Terminal of u kunt downloaden iTerm2, die is als een mooie, chique Terminal. Als je naar manual.cs50.net zie je een link naar Notepad + +, dat is wat u kunt gebruiken op een pc. Hiermee kunt u SFTP van Notepad + +, die in feite SSH. Wat dit zal u laten doen is lokaal bewerken van uw bestanden, en dan wanneer u maar wilt om hen te redden, zal het op te slaan in nice.fas, waar u kunt dan lopen ze. En het equivalent op een Mac gaat worden TextWrangler. Dus het laat je hetzelfde doen. Het laat je lokaal bewerken bestanden en sla deze op nice.fas, waar u kunt dan lopen ze. Dus als je ooit vast zonder een apparaat, moet u deze opties om nog te doen uw probleem sets. Het enige probleem zal zijn dat je niet naar de CS50 bibliotheek omdat nice.fas heeft geen standaard dat. U kunt het downloaden van de CS50 bibliotheek - Ik denk niet dat ik heb dat op dit punt. U kunt het downloaden van de CS50 bibliotheek en kopieer het naar nice.fas, of ik denk dat op dit punt hebben we het niet toch meer gebruiken. Of als we dat doen, dan kunt u voor de tijd die wordt vervangen door een de implementaties van de functies in de CS50 bibliotheek toch. Dus dat niet zo'n groot beperking. En dat is dat. Ik ga nu terug naar het apparaat, we zullen alles doen wat in het apparaat. Als we kijken naar onze rubriek vragen, in het begin, zoals ik al zei in mijn e-mail, We moeten praten over de ene korte je moest kijken. We hebben de heroriëntatie & Buizen en deze drie vragen. Om die stroom niet functioneert als printf schrijven standaard? Dus stroom. Wat is een stream? Een stroom is in principe net als het is gewoon een aantal - Het is niet eens een bron van 1s en 0s. De stroom het om vraagt ​​is hier standaard uit. En zo standaard out is een stroom die bij het schrijven van het, het op het scherm. Standaard uit, door stroom, betekent dit dat je gewoon schrijven 1s en 0s aan, en het andere uiteinde van standard leest alleen van die stroom. Het is gewoon een reeks van 1s en 0s. U kunt schrijven naar streams of je kunt lezen uit beken afhankelijk van de stroom in werkelijkheid is. De andere twee standaard streams zijn standaard in en standaard fout. Standaard in is wanneer u getString, is het voor u klaar voor het invoeren van spullen. Dus het wachten op je, het is eigenlijk te wachten op standaard in, dat is echt wat je krijgt als je typt op het toetsenbord. Je typt in een standaard inch De standaardfout is in principe gelijk aan standaard uit, maar het is gespecialiseerd in dat wanneer u afdrukt naar standaardfout, je moet alleen foutmeldingen af ​​te drukken die dus je kunt onderscheid maken tussen gewone berichten naar het scherm versus foutmeldingen, afhankelijk van het feit of ze gingen naar standaard uit of standaard fout. Bestanden ook. Standaard uit, standaard in, en standaardfout zijn gewoon speciale streams, maar echt een bestand, wanneer u een bestand opent, wordt het een stroom van bytes waar je kunt gewoon lezen van die stroom. U, voor het grootste deel kunnen denk van een bestand als een stroom bytes. Dus wat streams ze schrijven naar standaard? Standaard uit. Wat is het verschil tussen> en >>? Heeft iemand op voorhand kijken naar de video? Oke. > Gaat worden hoe je stuurt in bestanden, en >> gaat ook om output te sturen naar bestanden, maar het in plaats daarvan zal toevoegen aan het bestand. Bijvoorbeeld, laten we zeggen dat ik toevallig aan dict hebben hier, en de enige dingen binnenkant van dict is kat, kat, hond, vis, hond. Een opdracht die u op de opdrachtregel is kat, dat is gewoon om af te drukken wat er in een bestand. Dus als ik kat dict zeggen, gaat het aan de kat, kat, hond, vis, hond af te drukken. Dat is alles wat kat doet. Dat betekent dat het afgedrukt op standaard uit kat, kat, hond, vis, hond. Als ik in plaats daarvan wil die omleiden naar een bestand, kan ik> en deze om te buigen naar wat het bestand is. Ik bel het bestand bestand. Dus als ik nu ls, zal ik zien dat ik heb een nieuw bestand genaamd bestand. En als ik open it up, gaat het om precies wat knuppel in op de opdrachtregel. Dus nu als ik dat doe dat nog eens, dan gaat de output omleiden in het dossier, en ik ga precies hetzelfde hebben. Dus technisch gezien, het volledig laten voorgaan wat we hadden. En we zullen zien of ik veranderen dict, ik haalde hond. Als we nu cat dict in bestand opnieuw, we gaan naar de nieuwe versie met hond laten verwijderen. Dus het overschrijft volledig het. In plaats daarvan, als we >>, het gaat om het bestand toe te voegen. Nu, het openen van bestanden, dan zien we dat we hebben net twee keer hetzelfde afgedrukt want het was daar een keer, dan zijn we gehecht aan de minuut. Dus dat is wat> en >> doen. Heeft de volgende vragen - Het maakt niet vragen. De andere die we hebben is <, die, indien> redirects standaard uit, je doet 2>, dat is omgeleid standaardfout. Dus als er iets ging naar standaardfout, zou het niet komen gebracht txt2. Maar let op als ik dat doe 2>, dan is het nog steeds afdrukken Hallo, Rob! aan de opdrachtregel want ik ben alleen omgeleid standaardfout, ik ben niet omgeleid standaard uit. Standaardfout en standaard uit zijn verschillend. Als je wilde eigenlijk schrijven standaardfout, dan kon ik dit veranderen als fprintf naar stderr. Dus printf, standaard afdrukken op standaard uit. Als ik handmatig wilt afdrukken naar standaardfout, dan moet ik fprintf gebruiken en opgeven wat ik wil afdrukken. Als in plaats daarvan heb ik fprintf stdout, dan is dat in principe gelijk aan printf. Maar fprintf de standaard fout. Dus nu, als ik deze te sturen naar txt2, Hallo, Rob! is nog steeds het krijgen afgedrukt op de opdrachtregel omdat het wordt steeds afgedrukt op standaard fout en ik ben alleen omgeleid standaard uit. Als ik nu redirect standaardfout, nu is het niet te krijgen gedrukt, en txt2 gaat worden Hallo, Rob! Dus nu kunt u afdrukken uw werkelijke fouten te standaardfout en print uw regelmatige berichten naar standaard uit. En dus als je je programma uit te voeren, kunt u het als. / Hello dit type met de 2> zodat uw programma gaat normaal lopen, maar eventuele foutmeldingen die je krijgt kun je later in uw foutenlogboek, zodat fouten, en dan later kijken en je fouten bestand zal de eventuele fouten die gebeurd is. Vragen? De laatste is pijp, die u kunt bedenken als het nemen van de standaard uit een opdracht en maakt het de standaard van het volgende commando. Een voorbeeld is hier echo is een command line ding dat is gewoon gaan echo alles wat ik als argument. Ik zal niet aanhalingstekens. Echo blah, blah, blah is gewoon gaan bla, bla, bla af te drukken. Vroeger, toen ik zei dat ik moest Rob zetten in een txt-bestand want ik kan alleen txt-bestanden doorsturen, in plaats daarvan, / als ik echo Rob en dan pijp het in. / hello, dat zal ook hetzelfde doen soort dingen. Dit is het nemen van de uitvoer van deze opdracht, echo Rob, en het te gebruiken als input voor. / hello. U kunt denken aan het als eerste echo Rob redirect naar een bestand en voer in. / hello dat bestand dat was gewoon uitgevoerd. Maar het duurt het tijdelijke bestand uit het beeld. Vragen over zeggen? De volgende vraag gaat deze te betrekken. Wat pijpleiding kan je gebruiken om het aantal unieke namen in een bestand met de naam names.txt vinden? De commando's gaan we hier willen gebruiken zijn uniek, dus uniq, en dan wc. U kunt dit doen man uniq om daadwerkelijk te kijken naar wat dat doet, en het is gewoon naar de aangrenzende bijpassende lijnen filteren van de ingang. En de mens wc gaat naar de nieuwe regel, woord en bytes voor elk bestand af te drukken. En de laatste gaan we willen gebruiken is een soort, die gaat gewoon soort lijnen van txt-bestand. Als ik een paar txt bestand, names.txt, en het is Rob, Tommy, Joseph, Tommy, Joseph, RJ, Rob, wat ik hier wil doen is het vinden van het aantal unieke namen in dit bestand. Dus wat moet het antwoord zijn? >> [Student] 4. >> Ja. Het moet 4 zijn, omdat Rob, Tommy, Joseph, RJ zijn de enige unieke namen in dit bestand. De eerste stap, als ik gewoon het aantal woorden op names.txt, Dit is eigenlijk vertelt me ​​alles. Dit is eigenlijk afdrukken - laten we eens kijken, man wc - nieuwe regels, woorden en bytes. Als ik alleen de zorg over lijnen, dan kan ik gewoon doen wc-l names.txt. Dus dat is stap 1. Maar ik wil niet wc-l names.txt omdat names.txt net bevat alle namen, en ik wil om te filteren op alle niet-unieke degenen. Dus als ik dat doe uniq names.txt, betekent dat niet helemaal geef me wat ik wil omdat de gedupliceerde namen zijn er nog steeds. Waarom is dat? Waarom is uniq niet doen wat ik wil? [Student] De duplicaten zijn niet [onverstaanbaar] >> Ja. Denk aan de man pagina voor uniq zegt filter aangrenzende overeenkomende regels. Ze zijn niet aangrenzend, dus het zal niet filteren. Als ik sorteer ze eerst, wordt sorteren names.txt gaan in elkaar te zetten alle dubbele lijnen. Dus nu soort names.txt is dat. Ik ga wil dat gebruiken als input voor Uniq, dat is | uniq. Dat geeft me Joseph, RJ, Rob, Tommy, en ik wil dat gebruiken als input voor wc-l, die zal mij 4. Zoals hier staat, kun je wat pijplijn gebruiken? U kunt dit doen een heleboel dingen zoals het gebruik van een reeks opdrachten waar u gebruik maken van de output van een commando als invoer voor de volgende opdracht. U kunt dit doen een heleboel dingen, een hoop slimme dingen. Vragen? Oke. Dat is het voor pipes en redirection. Nu gaan we door naar de eigenlijke dingen, de codering spul. Binnenkant van deze PDF, zie je deze opdracht, en je wilt deze opdracht uit te voeren in uw apparaat. wget is de opdracht voor net iets van het internet, in principe, dus wget en deze URL. Als je naar deze URL in je browser, zou het downloaden dat bestand. Ik klikte op, dus het het bestand heeft gedownload voor mij. Maar het schrijven van wget van dat ding in de terminal is gewoon om het te downloaden naar uw toestel. Ik heb section5.zip, en je wilt section5.zip unzip, die zal u een map met de naam section5, die gaat naar alle bestanden gaan we gebruiken vandaag de dag binnen van het hebben. Omdat deze programma's 'bestand namen al aangeven, ze zijn een beetje buggy, dus je missie is om erachter te komen waarom het gebruik van gdb. Heeft iedereen hebben ze gedownload / weet hoe je ze gedownload in hun apparaat? Oke. Running ./buggy1 zal het zeggen Segmentation fault (core dumped), die elke keer krijg je een segfault, het is een slechte zaak. Onder welke omstandigheden krijg je een segfault? [Student] dereferentie een null pointer. >> Ja. Dat is een voorbeeld. Dereferentie een null pointer je gaat een segfault te krijgen. Wat een segfault betekent is dat je geheugen te raken moet je niet aanraken. Dus dereferentie een null pointer is ontroerend adres 0, en eigenlijk, alle computers tegenwoordig zeggen dat adres 0 is het geheugen moet je niet aanraken. Dus dat is waarom dereferentie een null pointer resulteert in een segfault. Als je toevallig niet initialiseren een pointer, dan heeft het een vuilnis waarde, en dus wanneer u probeert om dereferentie het, naar alle waarschijnlijkheid je raakt het geheugen dat is in het midden van nergens. Als je toevallig je geluk en het vuilnis waarde toevallig ergens wijzen op op de stapel of iets, dan wanneer je dereference die aanwijzing die u niet hebt geïnitialiseerd, er zal niets mis gaan. Maar als het te wijzen op, laten we zeggen, ergens tussen de stack en de heap, of het is te wijzen alleen maar om ergens dat niet is gebruikt door uw programma nog niet, dan ben je aanraken geheugen moet je niet aanraken en je segfault. Als je schrijft een recursieve functie en het recurses te vaak en je stack te groot wordt en de stapel botst tegen dingen dat het niet moet worden botsing met, je raakt het geheugen moet je niet aanraken, dus je segfault. Dat is wat een segfault is. Het is ook dezelfde reden dat als je een string als - laten we terug gaan naar het vorige programma. In hello.c--Ik ben gewoon naar iets anders te maken. char * s = "hello world!"; Als ik * s = iets of s [0] = 'X'; dus zorg hallo,. / hello, waarom dat segfault? Waarom is dit segfault? Wat zou je verwachten dat er gebeurt? Als ik dat deed printf ("% s \ n", s); wat zou je verwachten dat moet worden afgedrukt? [Student] X hello. >> Ja. Het probleem is dat wanneer je verklaart een string als deze, s is een pointer die gaat naar de stapel, s en wat wijst naar Hierdoor string die is opgenomen in leesgeheugen. Dus gewoon door de naam, alleen-lezen geheugen, moet je op het idee dat als je probeert te veranderen wat daar in alleen-lezen geheugen, je doet iets wat je niet zou moeten doen met het geheugen en u segfault. Dit is eigenlijk een groot verschil tussen char * s en char s []. Dus char s [], nu deze string zal worden op de stapel gelegd, en de stack is niet alleen-lezen, wat betekent dat dit zou moeten werken prima. En het doet. Vergeet niet dat als ik dat doe char * s = "hello world!", S zelf is op de stapel maar s punten ergens anders, en dat ergens anders gebeurt er als alleen-lezen. Maar char s [] is gewoon iets op de stapel. Dus dat is een ander voorbeeld van een segfault gebeurt. We zagen dat ./buggy1 resulteerde in een segfault. In theorie zou je niet meteen kijken buggy1.c. In plaats daarvan zullen we kijken naar het door gdb. Merk op dat als je Segmentation fault (core dumped), krijg je dit bestand dan hier de naam kern. Als we ls-l, zullen we zien dat kern is meestal een vrij groot bestand. Dit is het aantal bytes van het bestand, zodat het lijkt alsof het is 250-iets kilobytes. De reden hiervoor is dat hetgeen de kern is dump is wanneer uw programma vastloopt, de toestand van het geheugen van uw programma gewoon wordt gekopieerd en geplakt in dit bestand. Het wordt gedumpt in dat bestand. Dit programma, dat deze actief is, is er gebeurd met een geheugengebruik van ongeveer 250 kilobytes hebben, en dus dat is wat gedumpt in dit bestand. Nu kun je kijken naar dat bestand als we gdb buggy1 kern. We kunnen gewoon doen gdb buggy1, en dat zal gewoon opstarten gdb regelmatig, met behulp van buggy1 als zijn invoerbestand. Maar als je gdb buggy1 kern, dan is het specifiek gaat om het opstarten van gdb door te kijken naar die kern-bestand. En zeg je buggy1 betekent gdb weet dat die kern-bestand afkomstig is van de buggy1 programma. Dus gdb buggy1 kern gaat direct brengen ons naar de plaats waar het programma is er gebeurd met beëindigen. We zien hier Programma beëindigd met signaal 11, Segmentation fault. We toevallig een lijn van vergadering, die waarschijnlijk niet erg behulpzaam zien. Maar als je typt bt of backtrace, dat gaat om de functie te dat geeft ons de lijst van onze huidige stack frames. Dus backtrace. Het lijkt erop dat we maar twee stack frames. De eerste is onze belangrijkste stack frame, en de tweede is de stack frame deze functie die we toevallig in, die eruit ziet als we alleen de assemblage-code voor hebben. Dus laten we terug gaan naar onze belangrijkste functie, en om dat te doen kunnen we gestel 1 doen, en ik denk dat we ook doen beneden, maar ik bijna nooit naar beneden - of omhoog. Ja. Op en neer. Up brengt u een stapel frame, naar beneden brengt je in een stack frame. Ik heb de neiging om nooit gebruiken. Ik specifiek zeggen frame 1, dat naar het frame met 1. Frame 1 gaat ons in grote stack frame, en het zegt hier de lijn van code die we toevallig op. Als we wilden een paar regels code, kunnen we zeggen lijst, en dat gaat ons alle regels code eromheen. De lijn die we segfault op was 6: if (strcmp ("CS50 rocks", argv [1]) == 0). Als het niet voor de hand bent, kun je meteen het krijgen van hier gewoon door te denken waarom het segfault. Maar we kunnen nog een stap verder en zegt: "Waarom zou argv [1] segfault?" Let's afdrukken argv [1], en het lijkt alsof het 0x0, dat is de null pointer. We strcmping CS50 rotsen en nul, en dus dat gaat segfault. En waarom is argv [1] null? [Student] Omdat we niet geven elke command-line argumenten. Ja. We hebben niet geven geen command-line argumenten. Dus ./buggy1 is alleen maar te hebben argv [0] zijn ./buggy1. Het gaat niet om een ​​argv [1], zodat dat gaat segfault. Maar als, in plaats daarvan, ik doe gewoon CS50, het gaat om te zeggen Je krijgt een D want dat is wat het verondersteld wordt te doen. Kijkend naar buggy1.c, het is de bedoeling om af te drukken "Je krijgt een D" - Als argv [1] is niet "CS50 rocks", "Je krijgt een D", anders "Je krijgt een A!" Dus als we een A willen, moeten we deze te vergelijken als waar, Dit betekent dat het vergeleken met 0. Dus argv [1] moet worden "CS50 rocks". Als u wilt dat te doen op de opdrachtregel, moet u \ te gebruiken om de ruimte te ontsnappen. Dus CS50 \ rotsen en U krijgt een A! Als u dit niet doet de backslash, waarom dit niet werkt? [Student] Het is twee verschillende argumenten. >> Ja. Argv [1] zal worden CS50, en argv [2] zal worden rotsen. Oke. Nu ./buggy2 gaat weer segfault. In plaats van het openen van het met haar core-bestand, zullen we gewoon open buggy2 direct, dus gdb buggy2. Als we nu alleen nog maar ons programma, dan is het gaan zeggen Programma ontvangen signaal SIGSEGV, dat is de segfault signaal, en dit is waar het is gebeurd gebeuren. Kijkend naar onze backtrace, zien we dat we in de functie oh_no, die werd genoemd door de functie dinky, die werd genoemd door de functie binky, die werd genoemd door de belangrijkste. We zien ook de argumenten voor deze functies. Het argument voor dinky en Binky was 1. Als we een lijst van de functie oh_no, zien we dat oh_no is gewoon char ** s = NULL doen; * S = "BOOM"; Waarom zou dat niet? [Student] U kunt niet dereference de null pointer? >> Ja. Dit wordt alleen maar zeggen s NULL is, ongeacht of die toevallig een char **, die, afhankelijk van hoe je interpreteren, kan het een pointer naar een pointer naar een string of een array van strings. Het is s NULL is, dus * s is dereferentie een null pointer, en dus dit gaat crashen. Dit is een van de snelste manieren kunt u eventueel segfault. Het is gewoon waarbij een null pointer en onmiddellijk segfaulting. Dat is wat oh_no doet. Als we samen een frame, dan zijn we van plan om in de functie die oh_no genoemd. Ik moet naar beneden doen. Als u geen een opdracht en je gewoon op ENTER drukt, zal het gewoon herhaalt u de vorige opdracht die u liep. We zijn in frame 1. Aankondiging Dit frame, we hier zien is onze functie. U kunt weer hit list, of u kunt de lijst 20 doen en het zal een lijst van meer. De functie dinky zegt dat als i 1 is, ga dan naar de oh_no functie, anders heen te gaan naar de slinky functie. En we weten i is 1, omdat we toevallig zie hierboven dat dinky werd aangeroepen met het argument 1. Of je kunt gewoon printen i en het zal zeggen dat ik: 1. We zijn momenteel in dinky, en als we gaan een ander frame, we weten dat we eindigen in binky. Up. Nu zijn we in binky. Notering van deze functie - de lijst van voor de rust sneed me af - het begon als i 0, dan gaan we noemen het oh_no, anders dinky noemen. We weten dat ik was 1, dus het heet dinky. En nu zijn we terug in de belangrijkste, en de belangrijkste is gewoon gaat worden int i = rand ()% 3; Dat is gewoon om u een willekeurig nummer dat is 0, 1, of 2. Het gaat om binky gesprek met dat nummer, en het zal terugkeren 0. Kijkend naar deze, gewoon wandelen door het programma handmatig zonder stromend onmiddellijk, je zou een breekpunt ingesteld op de belangrijkste, wat betekent dat wanneer we het programma te starten uw programma loopt totdat het een breekpunt. Dus het uitvoeren van het programma, zal het uit te voeren en dan zal de belangrijkste functie raken en stoppen. Nu we binnen van de belangrijkste, en stap of naast ons gaat brengen naar de volgende regel code. U kunt dit doen stap of het volgende. Volgende Slaan, nu heb ik is ingesteld op rand ()% 3, dus we kunnen de waarde van i te drukken, en het zal zeggen dat ik: 1. Nu is het er wel toe doet of we volgende of stap te gebruiken. Ik denk dat het ertoe deed in de vorige, maar we zouden willen naar de volgende te gebruiken. Als we stap, stappen we in de functie, wat betekent kijken naar de werkelijke ding wat er gebeurt binnenkant van Binky. Als we de volgende, dan betekent dit gaan over de functie en ga je gewoon naar de volgende regel code op de hoofd-functie. Hier op deze lijn, was ik op waar het zei rand ()% 3; als ik dat deed stap, zou het gaan in de uitvoering van de rand en kijken naar wat er daar gebeurt, en ik kon gaan door de rand-functie. Maar kan me niet schelen over de rand functie. Ik wil gewoon naar de volgende regel code te gaan in de belangrijkste, dus ik gebruik volgende. Maar nu doe ik de zorg over de binky functie, dus ik wil om de stap naar dat. Nu ben ik in binky. De eerste regel van de code gaat zeggen indien (i == 0), neem ik een stap, we zien dat we terecht bij dinky. Als we de lijst dingen, zien we dat het gecontroleerd is i = 0. i is gelijk aan 0, zodat het naar de andere toestand, die zal dinky (i) te bellen. Je zou in de war raken. Als je gewoon rechtstreeks naar deze lijnen, zou je denken als (i == 0), oke, dan nam ik een stap en nu ben ik bij dinky (i), je zou denken dat moet betekenen i = 0 of zoiets. Nee, het betekent gewoon dat het weet dat het kan direct vasthouden aan de lijn dinky (i). Omdat i niet 0 is, wordt de volgende stap niet eindigen bij de andere. Else is niet een lijn het gaat om te stoppen bij. Het zal alleen maar om naar de volgende regel kan daadwerkelijk uit te voeren, dat is dinky (i). Stap in dinky (i), zien we als (i == 1). We weten i = 1, dus toen we stappen, we weten dat we gaan eindigen in oh_no omdat i = 1 roept de functie oh_no, die u kunt stap in, die gaat om char ** s = onmiddellijk NULL en "BOOM". En vervolgens daadwerkelijk te kijken naar de uitvoering van buggy2, dit wordt ik net een willekeurig getal - 0, 1, of 2 - bellen binky, die, indien i 0 het noemt oh_no, anders dinky noemt, die komt hier. Als ik 1 is, bellen oh_no, anders bellen slinky, die hier komen, als ik 2 is, bel oh_no. Ik weet niet eens dat er een manier - Ziet iemand een manier om dit een programma dat niet zal segfault? Omdat tenzij ik mis iets, als ik gelijk is aan 0, dan heb je meteen segfault, anders ga je naar een functie die als ik is 1 segfault u, anders ga je naar een functie waar als ik is 2 u segfault. Dus niet uit wat je doet, je segfault. Ik denk dat een manier om hem vast te zetten zou zijn in plaats van het doen van char ** s = NULL, je zou kunnen malloc ruimte voor die string. We konden doen malloc (sizeof) - sizeof wat? [Student] (char) * 5? >> Lijkt dit toch? Ik neem aan dat dit zal werken als ik eigenlijk liep, maar het is niet wat ik zoek. Kijk naar de aard van de s. We voegen int *, dus int * x. Ik zou doen malloc (sizeof (int)). Of als ik een blok van 5 wilde, zou ik doen (sizeof (int) * 5); Wat als ik een int ** hebben? Wat zou ik malloc? [Student] Grootte van de aanwijzer. >> Ja. (Sizeof (int *)); Hetzelfde hier beneden. Ik wil (sizeof (char *)); Dit gaat de ruimte toe te wijzen voor de pointer die wijst naar "BOOM". Ik hoef niet om ruimte toe te wijzen voor "BOOM" zelf want dit is eigenlijk gelijk aan wat ik al eerder zei van char * x = "BOOM". "BOOM" bestaat reeds. Het gebeurt bestaan ​​in het alleen-lezen gebied van geheugen. Maar het bestaat al, wat betekent dat deze regel code, als s een char **, dan * s is een char * en je bent het instellen van deze char * om te wijzen op "BOOM". Als ik wilde "BOOM" in s te kopiëren, dan zou ik moeten ruimte s toe te wijzen. Ik doe * s = malloc (sizeof (char) * 5); Waarom 5? Waarom niet 4? Het ziet eruit als "BOOM" is 4 karakters. >> [Student] De nul karakter. Ja. Al uw snaren gaan naar de nul-karakter nodig hebben. Nu kan ik iets doen als strcat - Wat is de functie voor het kopiëren van een string? [Student] CPY? >> Strcpy. man strcpy. Dus strcpy of strncpy. strncpy is een beetje veiliger, omdat u opgeeft, kan precies hoeveel tekens, maar hier maakt het niet uit omdat we weten. Dus strcpy en kijk in de argumenten. Het eerste argument is onze bestemming. Het tweede argument is onze bron. We gaan kopiëren naar onze bestemming * s de aanwijzer "BOOM". Waarom zou je dit willen doen met een strcpy in plaats van alleen wat we hebben gehad van * s = "BOOM"? Er is een reden waarom je zou willen om dit te doen, maar wat is die reden? [Student] Als je iets wilt veranderen in "BOOM". >> Ja. Nu kan ik iets als het doen [0] = 'X'; omdat s wijst op de heap en ruimte op de heap dat is naar wijst is een pointer naar meer ruimte op de heap, die het opslaan van "BOOM". Dus dit exemplaar van "BOOM" wordt opgeslagen in de heap. Er zijn technisch gezien twee exemplaren van "boom" in ons programma. Daar is het eerste dat is gewoon gegeven door deze "BOOM" string constante, en het tweede exemplaar van "boom", strcpy creëerde de kopie van "boom". Maar het exemplaar van "BOOM" wordt opgeslagen op de heap, en de hoop je bent vrij om te veranderen. De hoop is niet alleen-lezen, dus dat betekent dat s [0] gaat om u te laten de waarde van "boom". Het gaat om je te laten veranderen die tekens. Vragen? Oke. Over te gaan tot buggy3, laten we gdb buggy3. We draaien en we zien we een segfault. Als we backtrace zijn er slechts twee functies. Als we gaan in onze belangrijkste functie, zien we dat we een segfault op deze lijn. Dus gewoon kijken naar deze lijn, int lijn = 0 voor (; fgets dit spul is niet gelijk aan NULL; lijn + +). Onze vorige frame heette _IO_fgets. Je zult zien dat er veel met ingebouwde C-functies, dat wanneer u de segfault te krijgen, zal er echt cryptische functienamen als dit _IO_fgets. Maar dat gaat hebben betrekking op deze fgets oproep. Ergens binnen hier, zijn we segfaulting. Als we kijken naar de argumenten om fgets, kunnen we afdrukken buffer. Laten we af te drukken als een - Oh, nee. Print is niet van plan om precies zoals ik het wil werken. Laten we eens kijken naar de actuele programma. Buffer is een karakter array. Het is een karakter array van 128 tekens. Dus als ik zeg afdrukbuffer, het gaat om die 128 tekens af te drukken, dat ik denk dat is wat er verwacht wordt. Wat ik was op zoek naar is af te drukken het adres van de buffer, maar dat betekent niet echt vertellen mij veel. Dus als ik toevallig zeg hier x buffer, het toont mij 0xbffff090, die, als je nog van vroeger of een bepaald punt, Oxbffff heeft de neiging om een ​​stack-achtige regio. De stapel heeft de neiging om ergens beginnen net onder 0xc000. Alleen al door het zien van dit adres, ik weet dat buffer gebeurt op de stapel. Opnieuw opstarten van mijn programma, aanloop,, bufferen we zagen was deze opeenvolging van karakters die zijn vrij veel betekenis. Dan afdrukken-bestand, wat doet-bestand eruit? [Student] Null. >> Ja. Bestand is een van het type FILE *, dus het is een pointer, en de waarde van die wijzer is null. Dus fgets gaat proberen om van die pointer lezen op een indirecte manier, maar om toegang tot die pointer, moet het dereference. Of, om toegang te krijgen wat moeten wijzen naar het referentie aan het. Dus het is dereferentie een null pointer en segfaults. Ik had herstart het daar. Als we breken op onze belangrijkste punt en lopen, de eerste regel van de code is char * filename = "nonexistent.txt"; Dat geeft een vrij grote hint over de vraag waarom dit programma niet. Typen naast brengt me naar de volgende regel, waar ik dit bestand openen, en dan onmiddellijk ik in onze lijn, waar ooit ik raakte volgende, het gaat om segfault. Wil iemand te gooien een reden waarom we zouden kunnen worden segfaulting? [Student] Bestand bestaat niet. >> Ja. Dit wordt verondersteld een hint te zijn dat wanneer u een bestand opent dat u nodig hebt om te controleren of het bestand daadwerkelijk bestaat. Dus hier, "nonexistent.txt"; Als we fopen bestandsnaam voor het lezen, wij dan moeten zeggen if (file == NULL) en zeg printf ("Bestand bestaat niet!" of - beter nog - filename); return 1; Dus nu kijken we of het NULL voordat u daadwerkelijk voort te zetten en probeert te lezen van dat bestand. We kunnen herscheppen het gewoon om dat dat werkt. Ik was van plan om een ​​nieuwe regel te nemen. Nu nonexistent.txt bestaat niet. Je moet altijd controleren op dit soort dingen. Je moet altijd controleren of fopen geeft NULL. U moet altijd controleren om ervoor te zorgen dat malloc niet NULL terug, of anders je segfault. Nu buggy4.c. Running. Ik gok deze wacht op invoer of eventueel oneindig looping. Ja, het is oneindig looping. Dus buggy4. Het lijkt erop dat we oneindige lus. We kunnen breken bij grote, start ons programma. In gdb, zolang de afkorting u eenduidig of speciale afkortingen die zij leveren voor u, dan kunt u gebruik maken van n naar de volgende te gebruiken in plaats van het hebben van te typen naast de hele weg. En nu ik raakte een keer n, kan ik gewoon op Enter om door te gaan volgend in plaats van te slaan n Voer, n Voer, n Enter. Het lijkt alsof ik in een soort lus die array is het instellen van [i] op 0. Het lijkt erop dat ik nooit ben uitbreken van deze for-lus. Als ik afdrukken i, dus ik 2 is, dan zal ik de volgende te gaan. Ik zal afdrukken i, i 3 is, dan zal ik de volgende te gaan. Ik zal afdrukken i en i is 3. Daarna drukt u i, i 4. Eigenlijk, print sizeof (array), dus grootte van de array is 20. Maar het lijkt erop dat er een aantal speciale gdb commando om uit te gaan totdat er iets gebeurt. Het is als het instellen van een aandoening die op de waarde van de variabele. Maar ik weet niet meer wat het is. Dus als we blijven gaan - Wat zei je? Wat heb je te brengen? [Student] Heeft weer te geven voeg ik - >> Ja. Dus weer te geven ik kan helpen. Als we alleen maar weer te geven i, zal het hier zetten wat de waarde van i is dus ik hoef het niet te drukken elke keer. Als we gewoon heen zien we 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5. Er gaat iets vreselijk mis, en ik wordt weer op 0. Kijkend naar buggy4.c, zien we alles wat er gebeurt is int array [5]; for (i = 0; i <= sizeof (array); i + +) array [i] = 0; Wat zien we dat hier aan de hand? Als een hint, toen ik de gdb buggy4 doen - laten we breken de belangrijkste, run - Ik heb afdrukken sizeof (array) om te zien wat de conditie is waar ik moet eindelijk uitbreken. Waar ben ik? Heb ik lopen? Ik heb nog niet verklaren. Zo af te drukken sizeof (array) en dat is 20, waarvan verwacht wordt dat sinds mijn array is van maat 5 en het is van 5 gehele getallen, dus het hele ding zou moeten zijn 5 * sizeof (int) bytes, waar sizeof (int) heeft de neiging om 4. Dus sizeof (array) is 20. Wat moet dit zijn? [Student] gedeeld door sizeof (int). >> Ja, / sizeof (int). Het lijkt erop dat er nog steeds een probleem. Ik denk dat dit moet gewoon < want het is vrijwel altijd > [Bowden] Ja. Als we verder gaan dan het einde van ons aanbod, een of andere manier die ruimte dat we dwingende is het overschrijven van de waarde van i. En dus als we kijken naar buggy4, breken de belangrijkste, rennen, laten we drukken het adres van i. Het lijkt erop dat het is bffff124. Laten we nu eens af te drukken het adres van de array [0]. 110. Hoe zit het met [1]? 114. [2], 118. 11C, 120. array [5] is bfff124. Dus array [5] heeft hetzelfde adres als ik, wat betekent dat array [5] is i. Als ze hetzelfde adres hebben, zijn ze hetzelfde. Dus toen we array [5] ingesteld op 0, zetten we i op 0. En als u denkt over dit in termen van de stapel, int i wordt eerst verklaard, wat betekent dat ik krijgt wat ruimte op de stack. Dan array [5] wordt toegewezen, dus dan 20 bytes worden toegewezen op de stapel. Dus ik wordt eerst toegerekend, dan zijn deze 20 bytes krijgen toegewezen. Dus ik gebeurt er vlak voor de array, en door de manier waarop, zoals ik al zei vorige week, waar technisch de stapel naar beneden groeit, Wanneer u index in een array zijn we gegarandeerd dat de 0e positie in het stelsel altijd gebeurt voordat de eerste positie in de array. Dit is een soort van hoe ik trok het vorige week. Merk op dat aan de onderkant hebben we het adres 0 hebben en aan de top hebben we mailadres Max. De stapel wordt steeds groeit naar beneden. Laten we zeggen dat we wijzen i. Wij wijzen integer i, wat betekent dat laten we maar zeggen dat hier geheel getal i wordt toegewezen. Dan wijzen we ons aanbod van 5 gehele getallen, wat betekent dat onder die, omdat de stapel groeit naar beneden, krijg deze 5 gehele getallen toegewezen. Maar vanwege de manier waarop arrays werken, we gegarandeerd dat de eerste positie in de array altijd een adres dan het tweede punt in de array. Dus scala stand 0 heeft altijd eerst gebeuren in het geheugen, terwijl scala positie 1 moet gebeuren na dat en array positie 2 moet gebeuren daarna wat betekent dat scala stand 0 zou ergens gebeuren hier beneden, matrix positie 1 zou gebeuren daarboven omdat omhoog te bewegen betekent een hogere adressen aangezien de maximale adres is hier. Dus array [0] hier beneden, array [1] hier, array [2] hier, array [3] hier. Merk op hoe voordat we integer toegewezen i helemaal tot hier, als we verder en verder in ons aanbod, zijn we dichter en dichter bij onze integer i. Het is gewoon zo gebeurt het dat array [5], dat is een positie buiten ons aanbod, is precies waar integer ik toevallig worden toegewezen. Dus dat is het punt waar we toevallig het raken van de ruimte op de stack dat werd toegewezen voor integer i, en we zijn het instellen van die op 0. Dat is hoe dat werkt. Vragen? Ja. [Student] Never mind. Oke. [Student] Hoe voorkom je dit soort fouten? Deze soort van fouten? Gebruik geen C als programmeertaal. Gebruik een taal die array bounds checking heeft. Zolang je voorzichtig bent, je hoeft alleen maar om te voorkomen dat die voorbij de grenzen van de array. [Student] Dus hier toen we gingen langs de grenzen van de array - [Bowden] Dat is waar de dingen beginnen mis gaat. >> [Student] Oh, oke. Zolang u binnen het geheugen toegewezen voor de array, je bent in orde. Maar C heeft geen controle op fouten. Als ik dat doe array [1000], zal het graag gewoon wijzigen wat er ook gebeurt - Het gaat naar het begin van de array, dan gaat 1000 posities na en geeft deze 0. Het doet geen controle, zodat de oh, dit niet echt 1000 dingen in hebben. 1000 is veel verder dan wat ik zou moeten veranderen, terwijl Java of iets wat je krijgt scala out of bounds index of index out of bounds uitzondering. Dat is de reden waarom veel hoger niveau talen die dingen waar als je verder gaan dan de grenzen van de array, je faalt zodat je niet kunt veranderen dingen onder je en dan gaat het veel erger dan alleen het krijgen van een uitzondering zeggen dat je verder ging dan het einde van de array. [Student] En zo zouden we hebben net de <= gewoon > [Bowden] Ja. Het moet > [Student] Juist. Meer vragen? Oke. [Student] Ik heb een vraag. >> Ja. [Student] Wat is de eigenlijke array variabele? [Bowden] Zoals wat is array? Array zelf als symbool. Het is alleen het adres van de start van de 20 bytes dat we verwijzen. U kunt hierbij denken aan het als een pointer, maar het is een constante pointer. Zodra dingen krijgen gecompileerd, wordt de variabele array niet meer bestaat. [Student] Dus wat is het vinden van de grootte van de array? Grootte van array verwijst naar de grootte van dat blok dat symbool verwijst. Als ik iets doen als printf ("% p \ n", array); laten we voer het uit. Wat heb ik net verkeerd gedaan? Array 'array' verklaarde hier. Oh, hier. Clang is slim, en het gebeurt te merken dat ik de array als 5 elementen verklaard maar ik ben het indexeren in positie 1000. Het kan dat doen, omdat dit zijn slechts constanten. Het kan alleen zo ver gaan in het opmerken dat ik die verder gaan dan de grenzen van de array. Maar let op toen we hadden i onjuist zijn, het kan niet mogelijk te bepalen hoeveel waarden i zou kunnen nemen op, zodat het niet kan bepalen of i ging na het einde van de array. Dat is gewoon Clang slim zijn. Maar nu maken buggy4. Dus wat doe ik verkeerd? Impliciet verklaren bibliotheekfunctie 'printf'. Ik ga wilt # include . Oke. Nu draait buggy4. Het afdrukken van de waarde van de array zoals ik deed hier, af te drukken als een pointer prints iets dat er zo uitziet - bfb8805c - dat is wat mailadres dat is in de stapel-ish regio. Array zelf is een pointer, maar is geen echte pointer, aangezien een gewone pointer we kunnen veranderen. Array is slechts enkele constant. De 20 blokken van het geheugen beginnen bij adres 0xbfb8805c. Dus bfb8805c via dit adres +20--of denk ik -20 - is al het geheugen toegewezen voor deze array. Array, wordt de variabele zelf nergens opgeslagen. Wanneer u samenstellen, de compiler - hand zwaaien naar het - maar de compiler zal gewoon gebruiken waarvan het bedrijf weet array te. Het weet waar die array begint, en kan dus altijd gewoon dingen doen op het gebied van offsets van dat begin. Het hoeft niet een variabele zelf aan array te vertegenwoordigen. Maar als ik iets als int * p = array doen, nu p is een pointer die wijst naar die array, en nu p doet eigenlijk bestaan ​​op de stapel. Ik ben vrij om te p te veranderen. Ik kan p = malloc. Dus het oorspronkelijk gewezen op rij, nu het wijst op wat ruimte op de heap. Ik kan het niet array = malloc. Als Clang is slim, zal het tegen me schreeuwen recht uit de vleermuis. Eigenlijk ben ik er vrij zeker van gcc zou dit ook doen. Dus scala type 'int [5]' is niet overdraagbaar. U kunt geen iets om een ​​array-type omdat array is gewoon een constante. Het is een symbool dat referenties die 20 bytes. Ik kan het niet veranderen. [Student] En waar is de grootte van de array opgeslagen? [Bowden] Het is nergens opgeslagen. Het is als het compileren. Dus waar is de grootte van de array opgeslagen? U kunt alleen gebruik maken van sizeof (array) binnenkant van de functie die de array zelf verklaard. Dus als ik doe wat functie, foo, en ik doe (int array []) printf ("% d \ n", sizeof (array)); en dan naar beneden hier noem ik foo (array); binnenkant van deze functie - laten we voer het uit. Dit is Clang slim zijn weer. Het is me te vertellen dat sizeof op array functie parameter zal terugkeren grootte van 'int *'. Dit zou een fout zijn als het is niet wat ik wilde gebeuren. Laten we in feite uit te schakelen Werror. Waarschuwing. Waarschuwingen zijn prima. Het zal het compileren zolang het een waarschuwing. . / A.out gaat 4 drukken. De waarschuwing dat werd gegenereerd is een duidelijke indicatie van wat er mis ging. Deze int array wordt gewoon op sizeof (int *) af te drukken. Zelfs als ik array [5] hier in zet, is het nog steeds gewoon op sizeof (int *) af te drukken. Dus zodra je voorbij het in een functie, het onderscheid tussen arrays en pointers is niet-bestaand. Dit gebeurt als een array die werd uitgeroepen op de stapel, maar zodra we langs die waarde, dat 0xbf bla, bla, bla in deze functie, Dan pointer wijst die array op de stapel. Dus dat betekent dat sizeof alleen van toepassing op de functie die de array werd verklaard, wat betekent dat wanneer u het samenstellen van deze functie, wanneer Clang gaat door deze functie, ziet het array is een int array van grootte 5. Dus dan ziet ze sizeof (array). Nou, dat is 20. Dat is eigenlijk hoe sizeof werkt in principe voor bijna alle gevallen. Sizeof is geen functie, het is een operator. U hoeft niet bellen met de sizeof functie. Sizeof (int), zal de compiler gewoon vertalen dat naar 4. Heb je het? Oke. [Student] Dus wat is het verschil tussen sizeof (array) in hoofd-en in foo? Dit komt omdat we zeggen sizeof (array), dat is van het type int *, terwijl de array hier beneden is niet van het type int *, het is een int array. [Student] Dus als je de parameter in array [] in plaats van int * array, zou dat betekenen dat je nog zou kunnen scala veranderen want nu is het een pointer? [Bowden] Vind je deze? >> [Student] Ja. Kun je serie nu veranderen binnen de functie? [Bowden] Je kon scala veranderen in beide gevallen. In beide gevallen bent u vrij om te zeggen array [4] = 0. [Student] Maar je kunt scala punt te maken om iets anders? [Bowden] Oh. Ja. In beide gevallen - >> [student] Ja. [Bowden] Het onderscheid tussen array [] en een int * array, er is niemand. U kunt een aantal multidimensionale array ook hier voor een aantal handige syntax, maar het is nog steeds slechts een wijzer. Dit betekent dat ik vrij ben om array doen = malloc (sizeof (int)), en wijzen nu ergens anders. Maar net als hoe dit werkt voor eeuwig en altijd, het wijzigen van deze serie door het maken van het wijzen op iets anders verandert niets aan deze reeks hier beneden, want het is een kopie van het argument, het is niet een pointer naar dat argument. En eigenlijk, net als meer aanwijzingen dat het precies hetzelfde - we al zagen wat afdrukken serie prints - wat als we drukken het adres van de array of het adres van het adres van de array op een van deze? Laten we negeren deze. Oke. Dit is prima. Het is nu actief. / A.out. Printing array Het afdrukken het adres van de array hetzelfde zijn. Array werkt gewoon niet. Het weet wanneer u afdrukt scala, je het symbool dat verwijst naar die 20 bytes afdrukken. Het afdrukken van de adres van de array, nou ja, niet array niet bestaat. Het heeft geen adres, dus het is gewoon print het adres van die 20 bytes. Zodra je compileren naar beneden, zoals in uw gecompileerde buggy4. / A.out, array is onbestaande. Pointers bestaan. Arrays niet. De geheugenblokken die de matrix nog steeds, maar de variabele array en variabelen van dat type niet bestaan. Dat zijn net de belangrijkste verschillen tussen arrays en pointers worden zodra u de functie bellen, is er geen verschil. Maar de binnenkant van de functie die de array zelf wordt verklaard, sizeof werkt anders aangezien u afdrukt de grootte van de blokken in plaats van de grootte van het type, en je kunt het niet veranderen want het is een symbool. Het afdrukken van de zaak en het adres van de zaak drukt hetzelfde. En dat is het zowat. [Student] Zou je kunnen zeggen dat nog een keer? Ik zou iets gemist hebben. Afdrukken array en adres van array drukt hetzelfde, terwijl als je afdrukken van een pointer versus het adres van de aanwijzer, het enige wat drukt het adres van wat je te wijzen op, de andere drukt het adres van de aanwijzer op de stapel. U kunt een pointer, je kunt niet veranderen een reeks symbool. En sizeof aanwijzer gaat naar de grootte van die pointer-type af te drukken. Dus int * p sizeof (p) gaat tot 4 af te drukken, maar int array [5] Print sizeof (array) gaat tot 20 af te drukken. [Student] Dus int array [5] wordt afgedrukt 20? >> Ja. Dat is waarom de binnenkant van buggy4 wanneer het wordt gebruikt om sizeof (array) worden Dit deed i <20, dat is niet wat we wilden. We willen i <5. >> [Student] Oke. [Bowden] En dan, zodra je begint het passeren in de functies, als we dat deden int * p = array; in deze functie kunnen we in principe gebruik p en array op dezelfde manier, behalve de sizeof probleem en de veranderende probleem. Maar p [0] = 1, is hetzelfde als zeggen array [0] = 1; En zodra we foo (matrix) zeggen, of foo (p); binnenkant van de foo functie, dit is tweemaal dezelfde oproep. Er is geen verschil tussen deze twee gesprekken. Iedereen goed op dat? Oke. We hebben 10 minuten. We zullen proberen om door dit Hacker Typer programma, deze website, die vorig jaar uitkwam, of zoiets. Het is gewoon hoort te zijn als u typt willekeurig en print - Wat bestand het gebeurt te zijn geladen is wat het lijkt alsof je aan het typen bent. Het ziet eruit als een soort besturingssysteem code. Dat is wat we willen implementeren. U moet een binaire uitvoerbare naam hacker_typer dat duurt in een enkel argument, het bestand "hacker type." Het uitvoeren van de executable moet het scherm op en print een karakter uit de doorrekeningen in het bestand telkens wanneer de gebruiker op een toets drukt. Dus wat ingedrukte toets, moet het weg te gooien en in plaats daarvan drukt u een karakter uit het bestand dat is het argument. Ik zal vrij veel vertellen wat de dingen die we gaan moeten weten zijn. Maar we willen kijken op de termios bibliotheek. Ik heb nog nooit gebruik gemaakt van deze bibliotheek in mijn hele leven, dus het is zeer minimaal doeleinden. Maar dit gaat naar de bibliotheek die we kunnen gebruiken om weg te gooien het teken dat u geraakt worden tijdens het typen in standaard inch Dus hacker_typer.c, en we gaan willen # include . Als we kijken naar de man pagina voor termios - ik ben gok dat het aansluitpunt van OS of iets - Ik weet niet hoe het te lezen. Kijkend naar deze, het zegt op te nemen deze 2 bestanden, dus we zullen dat doen. Beginnen bij het begin, we willen nemen in een enkel argument, dat is het bestand dat we moeten openen. Dus wat wil ik doen? Hoe kan ik controleren om te zien ik heb een argument? [Student] Als argc het gelijk. >> [Bowden] Ja. Dus als (argc = 2!) Printf ("Gebruik:% s [-bestand te openen]"). Dus nu als ik zonder dit zonder een tweede argument - oh, ik heb de nieuwe lijn - zie je het zegt gebruik:. / hacker_typer, en dan het tweede argument moet het bestand dat ik wil openen zijn. Nu, wat moet ik doen? Ik wil lezen uit dit bestand. Hoe kan ik een bestand lezen? [Student] U opent het eerst. >> Ja. Dus fopen. Hoe ziet fopen eruit? [Student] Bestandsnaam. >> [Bowden] Bestandsnaam gaat worden argv [1]. [Student] En dan wat je wilt doen met het, zodat het - >> [Bowden] Ja. Dus als je niet meer weet, kan je gewoon doen man fopen, waar het gaat om een ​​const char * pad waarbij pad bestandsnaam te zijn, const char * mode. Als je toevallig niet meer weet wat mode is, dan kunt u zoeken naar mode. Binnenkant van man-pagina's, de slash is wat u kunt gebruiken om te zoeken naar dingen. Dus ik typ / mode om te zoeken naar de modus. n en N zijn wat je kunt om te fietsen gebruiken via de zoekmachine resultaten. Hier staat het argument mode wijst naar een string beginnen met een van de volgende sequenties. Dus r, Open tekstbestand om te lezen. Dat is wat we willen doen. Voor het lezen, en ik wil op te slaan dat. Het ding gaat een FILE * worden. Nu, wat wil ik doen? Geef me een seconde. Oke. Nu, wat wil ik doen? [Student] Controleer of het NULL. >> [Bowden] Ja. Elke keer dat u een bestand opent, zorg ervoor dat u met succes in staat bent om het te openen. Nu wil ik dat termios dingen te doen waar ik wil het eerst las mijn huidige instellingen en op te slaan die in iets, dan wil ik mijn instellingen wijzigen weg te gooien elk karakter dat ik typ, en dan wil ik die instellingen bij te werken. En dan aan het einde van het programma, ik wil om terug te keren naar mijn oorspronkelijke instellingen. Dus de struct gaat worden van het type termios, en ik ga twee van die wil. De eerste gaat worden mijn current_settings, en dan zijn ze gaan worden mijn hacker_settings. Ten eerste, ik ga wil mijn huidige instellingen op te slaan, dan ga ik willen hacker_settings bijwerken, en dan weg aan het eind van mijn programma, ik wil terug naar de huidige instellingen. Dus het opslaan van de huidige instellingen, de manier die werkt, we man termios. We zien dat we deze int tcsetattr, int tcgetattr hebben. Ik passeer in een termios struct door de aanwijzer. De manier waarop dit zal zien is - ik heb al vergeten wat de functie is aangeroepen. Kopieer en plak deze. Dus tcgetattr, dan wil ik pas in de struct dat ik de informatie opslaan in, die zal worden current_settings, en het eerste argument is de file descriptor voor het ding wil ik de eigenschappen van op te slaan. Wat de file descriptor is is net als iedere keer dat u een bestand opent, krijgt het een file descriptor. Toen ik fopen argv [1], het wordt een file descriptor die u verwijst wanneer u wilt lezen of schrijven. Dat is niet de file descriptor Ik wil hier gebruiken. Er zijn drie file descriptors u standaard, die standaard, standaard uit en standaardfout. Standaard, ik denk dat het standaard in is 0, standaard uit is 1, en standaard fout is 2. Dus wat wil ik de instellingen van veranderen? Ik wil de instellingen van wanneer ik raakte een teken te wijzigen, Ik wil dat het dat karakter weg te gooien in plaats van deze af te drukken op het scherm. Wat stream - standaard in, standaard uit, of standaardfout - reageert op dingen als ik typ op het toetsenbord? >> [Student] Standaard inch >> Ja. Dus ik kan dit doen 0 of wat ik kan doen stdin. Ik krijg de current_settings van standaard inch Nu wil ik deze instellingen bij te werken, dus eerst zal ik kopiëren naar hacker_settings wat mijn current_settings zijn. En hoe structs werk is zal het gewoon kopiëren. Dit kopieert alle van de velden, zoals je zou verwachten. Nu wil ik een aantal van de velden te werken. Kijkend naar termios, zou je moeten lezen veel van deze gewoon om te zien wat je zou willen zoeken, maar de vlaggen je gaat te willen zoeken naar zijn echo, zodat ECHO Echo ingevoerde tekens. Eerst wil ik stellen - ik heb al vergeten wat de velden zijn. Dit is wat de struct eruit ziet. Dus invoermodi ik denk dat we willen veranderen. We kijken naar de oplossing om ervoor te zorgen dat is wat we willen veranderen. We willen lflag veranderen om te voorkomen om te kijken door al deze. We willen de lokale modus te wijzigen. U zou hebben om door te lezen dit hele ding om te begrijpen waar alles hoort dat we willen veranderen. Maar het is de binnenkant van de lokale modes waar we gaan willen dat veranderen. Dus hacker_settings.cc_lmode is hoe het heet. c_lflag. Dit is waar we in bitwise operators. We zijn een beetje uit de tijd, maar we gaan er doorheen heel snel. Dit is waar we in bitwise operators, waar ik denk dat ik zei een tijd lang geleden dat wanneer je te maken kreeg met vlaggen, je gaat worden met behulp van logische bewerking veel. Elk bit in de vlag komt overeen met een soort van gedrag. Dus hier, deze vlag heeft een heleboel verschillende dingen, waar ze allemaal betekenen iets anders. Maar wat ik wil doen is gewoon uitschakelen van de bit die overeenkomt ECHO. Dus om dat uitzetten doe ik & = ¬ ECHO. Eigenlijk denk ik dat het is alsof Techo of zoiets. Ik ga gewoon opnieuw te controleren. Ik kan termios het. Het is gewoon ECHO. ECHO gaat een beetje. ¬ ECHO gaat betekenen alle bits zijn ingesteld op 1, wat betekent dat alle vlaggen zijn ingesteld op true behalve voor de ECHO bit. Door de beëindiging van mijn lokale vlaggen met deze, betekent alle vlaggen die momenteel zijn ingesteld op true zal nog steeds worden ingesteld op true. Als mijn ECHO vlag is ingesteld op true, dan wordt dit per se ingesteld op false op de ECHO vlag. Dus deze regel code wordt net naast de ECHO vlag. De andere lijnen van de code, zal ik gewoon kopiëren in het belang van tijd en dan uitleggen. In de oplossing, zei hij 0. Het is waarschijnlijk beter om expliciet te zeggen stdin. Merk op dat ik ook ben ECHO doet | icanon hier. Icanon verwijst naar iets apart, die canonieke functie betekent. Wat canonieke modus middel is meestal als je het typen van de command line, standaard in niet bewerkt niets totdat je raakt newline. Dus als je getString, typt u een heleboel dingen, dan heb je newline raken. Dat is wanneer het wordt verzonden naar standaard inch Dat is de standaard. Wanneer schakel ik canonieke mode, nu elk teken dat u op is wat wordt verwerkt, die meestal soort slecht omdat het langzaam deze dingen verwerken dat is waarom het is goed om het te bufferen in complete lijnen. Maar ik wil elk teken moet worden verwerkt want ik wil niet dat het wachten voor mij om newline raken voordat het verwerkt alle karakters heb ik te typen. Deze schakelt canonieke modus. Dit spul betekent alleen wanneer het echt verwerkt karakters. Dit betekent meteen verwerken, zodra ik ze te typen, te verwerken. En dit is de functie die mijn instellingen aan het bijwerken is voor standaard in, en TCSA betekent het nu doen. De andere opties zijn wachten tot alles dat op dit moment op de stream wordt verwerkt. Dat maakt eigenlijk niet uit. Net nu verander ik mijn instellingen te zijn wat op dit moment in hacker_typer_settings. Ik denk dat ik noemde het hacker_settings, dus laten we dat veranderen. Alles veranderen aan hacker_settings. Nu aan het einde van ons programma gaan we wilt terugkeren wat is nu de binnenkant van normal_settings, die zal net lijken & normal_settings. Merk op dat ik zijn niet veranderd een van mijn normal_settings sinds oorspronkelijk het krijgen van het. Dan gewoon veranderen ze terug, ik geef ze terug aan het eind. Dit was de update. Oke. Nu binnenkant van hier zal ik de code even uitleggen in het belang van de tijd. Het is niet zo veel code. We zien lezen we een personage uit het bestand. We noemden het f. Nu kunt u man fgetc, maar hoe fgetc gaat werken is gewoon dat het gaat om het karakter dat je net hebt gelezen of EOF terug te keren, dat overeenkomt met het einde van het bestand of een foutmelding voorkomt. We looping, blijven een teken uit het bestand te lezen, totdat we opraken van de tekens te lezen. En terwijl we dat doen, we wachten op een teken van de standaard inch Elke keer dat je iets typt op de opdrachtregel, dat is te lezen in een personage uit standaard inch Dan putchar is gewoon naar de char lezen we hier van het bestand naar standaard uit te zetten. U kunt de mens putchar, maar het is zomaar de standaard uit, het is het afdrukken van dat karakter. Je kan ook gewoon printf doen ("% c", c); Zelfde idee. Dat zal het grootste deel van ons werk te doen. Het laatste wat we gaan willen doen is gewoon fclose ons bestand. Als je niet fclose, dat is een geheugenlek. We willen het bestand dat we oorspronkelijk geopend fclose, en ik denk dat is het. Als we dat, ik heb al problemen. Laten we eens kijken. Wat heeft het klagen over? Verwachte 'int', maar argument is van het type 'struct _IO_FILE *'. We zullen zien of dat werkt. Alleen toegestaan ​​in C99. Augh. Oke, maken hacker_typer. Nu krijgen we meer bruikbare beschrijvingen. Dus gebruik van niet-aangegeven identifier 'normal_settings'. Ik heb niet noemen normal_settings. Ik noemde het current_settings. Dus laten we veranderen allemaal. Nu passeren argument. Ik zal dit aan 0 voor nu. Oke. . / Hacker_typer cp.c. Ik wist ook het scherm niet duidelijk in het begin. Maar je kunt terugkijken op het laatste probleem set om te zien hoe u het scherm leeg. Het is gewoon af te drukken sommige karakters terwijl dit te doen wat ik wil doen. Oke. En na te denken over waarom dit moest zijn 0 in plaats van stdin, # die worden gedefinieerd 0, Dit klaagt dat - Voordat toen ik zei dat er file descriptors, maar dan heb je ook je FILE *, een file descriptor is slechts een enkel geheel getal, terwijl een FILE * heeft een heleboel dingen die ermee verbonden zijn. De reden dat we nodig hebben om 0 te zeggen in plaats van stdin is dat stdin is een FILE * die verwijst naar het ding dat verwijst file descriptor 0. Dus zelfs hier als ik fopen doen (argv [1], ik krijg een FILE * terug. Maar ergens in dat bestand * is een ding die overeenkomt met de file descriptor voor dat bestand. Als je kijkt naar de man pagina voor open, dus ik denk dat je hoeft te doen man 3 open - nope - man 2 open - ja. Als je kijkt naar de pagina voor open, open is als een lager niveau fopen, en het is het retourneren van de eigenlijke file descriptor. fopen doet een heleboel dingen op de top van open, die in plaats van terug te keren op dat bestand tem geeft een hele FILE * pointer binnenkant van die onze kleine file descriptor. Dus standaard in verwijst naar de FILE * ding, terwijl 0 verwijst naar alleen de file descriptor standaard op zich. Vragen? [Lacht] Blew doorheen. Oke. We zijn klaar. [Lacht] [CS50.TV]