[Powered by Google Translate] [Oddíl 5 - Více Comfortable] [Rob Bowden - Harvard University] [To je CS50. - CS50.TV] Jak jsem řekl ve svém e-mailu, existuje mnoho věcí, které můžete použít jiné než zařízení skutečně dělat problémové soubory. Doporučujeme vám to v přístroji jen proto, že pak můžeme snáze pomůže a víme, jak to všechno bude fungovat. Ale jako jeden příklad, kde si můžete dělat věci, pokud, řekněme, nemáte přístup ke spotřebiči, nebo chcete pracovat v suterénu Science Center - které vlastně mají přístrojem příliš - Pokud chcete pracovat kdekoliv. Jedním z příkladů je viděli jste / slyšel SSH? SSH je v podstatě stejně jako připojení k něčemu. Vlastně, teď jsem SSHed do přístroje. Nikdy jsem pracovat přímo v přístroji. Zde je spotřebič, a když se podíváte dolů tady vidíte tuto IP adresu. Nikdy jsem pracovat v zařízení samotném; Vždycky jsem přijít na iTerm2 okna / terminálovém okně. Můžete SSH na tuto IP adresu, ssh jharvard@192.168.129.128. Pamatuji si, že počet velmi snadno, protože je to takový milý vzor. Ale to bude zeptejte se mě na mé heslo, a teď jsem ve spotřebiči. V podstatě, v tomto bodě, pokud otevřel terminál uvnitř spotřebiče samotného, toto rozhraní, ale měli byste použít ji, je přesně stejná jako rozhraní já používám tady, ale teď jsi SSHed. Nemusíte se připojit přes SSH na spotřebiče. Jeden příklad na jiném místě byste mohli SSH na je, že jsem si jistá, že máte ve výchozím nastavení - Oh. Větší. Všichni z vás by měl mít podle účtů výchozí FAS na serverech FAS. Pro mě, já bych SSH rbowden@nice.fas.harvard.edu. Je tě zeptat, že poprvé, a řeknete ano. Moje heslo je jen bude můj FAS heslo. A tak teď, jsem SSHed na pěkné servery, a můžu si dělat, co chci tady. Mnoho tříd můžete vzít, stejně jako 124, budou mít nahrajete věci sem skutečně předložit své problémové sady. Ale říct, že nemáte přístup k vašemu přístroji. Pak můžete dělat věci, jako je tady to bude říkat - To je jen naše část otázek. To se vás zeptá, jak to udělat ve spotřebiči. Místo toho jsem si jen to na serveru. Jdu k rozbalení, že. Problém bude, že jste zvyklí používat něco jako gedit nebo co uvnitř zařízení. Nebudete mít, že na FAS serveru. Je to všechno jenom bude to textové rozhraní. Takže byste mohli buď jeden, zkuste se naučit textového editoru, který oni mají. Mají Nano. Nano je obvykle velmi snadno ovladatelný. Můžete použít šipky a zadejte normálně. Takže to není těžké. Pokud chcete získat opravdu fantazie můžete použít Emacs, které jsem asi neměl otevřít, protože já ani nevím, jak ukončit Emacs. Control X, Control C? Jo. Nebo můžete použít Vim, což je to, co mám použít. A tak to jsou vaše možnosti. Pokud nechcete dělat, že můžete také, když se podíváte na manual.cs50.net-- Oh. Na počítači, můžete pomocí SSH PuTTY, které budete muset stáhnout zvlášť. Na Macu, stačí Terminal výchozí použití, nebo si můžete stáhnout iTerm2, který je jako pěkné, efektní terminálu. Pokud půjdete do manual.cs50.net uvidíte odkaz na Notepad + +, což je to, co můžete použít na počítači. To vám umožní SFTP z Notepad + +, který je v podstatě SSH. Co to vám umožní udělat, je upravovat své soubory lokálně, a pak pokaždé, když chcete uložit, bude to ušetří až nice.fas, kde pak můžete spustit. A odpovídá na Mac bude TextWrangler. Tak to vám umožní udělat to samé. To vám umožní upravovat soubory lokálně a uložit je do nice.fas, kde pak můžete spustit. Takže pokud jste někdy uvízl bez zařízení, máte tyto možnosti na stále dělat své problémové sady. Jeden problém bude, že nebudete mít CS50 knihovnu protože nice.fas nemá ve výchozím nastavení mají, že. Můžete buď stáhnout CS50 knihovny - Já si nemyslím, že je třeba, aby v tomto bodě. Můžete buď stáhnout CS50 knihovnu a zkopírujte jej do nice.fas, nebo jsem si, že v tomto bodě nemáme používat už tak jako tak. Nebo pokud ano, můžete v současné době jej nahradit Implementace těchto funkcí v CS50 knihovně tak jako tak. Takže by neměl být tak velký omezení. A to je, že. Vrátím se do spotřebiče nyní, budeme dělat vše, co ve spotřebiči. Při pohledu na naši sekci otázek, na začátku, jak jsem řekl ve svém e-mailu, musíme si promluvit o jedné zkratce jste se měli dívat. Máme přesměrování a trubky a tyto tři otázky. Chcete-li, které proud se funkce jako printf psát ve výchozím nastavení? Tak stream. Co je proud? Proud je v podstatě taková, že je to jen nějaký - Není to ani zdrojem 1s a 0s. Proud, který se ptá na tu je standardní výstup. A tak standardní výstup je proud, který při psaní na to, se objeví na obrazovce. Standardní, tím, že proud, znamená to, že stačí napsat 1s a 0s k němu, a druhý konec standardní výstup jen čte z tohoto proudu. Je to jen řetězec 1s a 0s. Můžete psát do vodních toků, nebo si můžete přečíst z potoků v závislosti na tom, co proud vlastně je. Zbylé dva Výchozí toky jsou standardně a standardní chyby. Standardní in je vždy, když to GetString, je to na vás čeká na vstup věci. Tak to vás čeká, je to vlastně čeká na standardu, což je opravdu to, co dostanete, když zadáte na klávesnici. Píšete do standardu palců Standardní chyba je v podstatě ekvivalentní standardní výstup, ale je to specializované v tom, že při tisku na standardní chybový výstup, jste měl tisknout pouze chybové zprávy, které takže můžete rozlišit mezi pravidelnými zprávami tištěných na obrazovku proti chybových zpráv v závislosti na tom, zda šli do standardní výstup nebo standardní chybou. Soubory taky. Standardní výstup, standardní v, a standardní chyba je jen zvláštní proudy, ale opravdu jakýkoli soubor, při otevření souboru, to se stává proud bajtů , kde můžete jen číst z tohoto proudu. Ty, pro nejvíce se rozdělit, může jen myslet souboru jako proud bajtů. Takže to, co toky se píší ve výchozím nastavení? Standardní výstup. Jaký je rozdíl mezi> a >>? Copak někdo sledovat video předem? Dobře. > Se bude, jak přesměrovat do souborů, a >> bude také přesměrovat výstup do souboru, ale je to místo bude připojit k souboru. Například, řekněme, že jsem náhodou dict tady, a jediný věci uvnitř dict je kočka, kočka, pes, ryba, pes. Jeden příkaz, který jste v příkazovém řádku je kočka, který je jen tak vytisknout to, co je v souboru. Takže když říkám kočka dict, že to bude tisknout kočka, kočka, pes, ryba, pes. To je vše, kočka dělá. To znamená, že pro tisk na standardní výstup kočka, kočka, pes, ryba, pes. Kdybych místo toho chcete přesměrovat, že do souboru, mohu použít> a přesměrovat ji na co je soubor. Zavolám soubor soubor. Takže teď, když já ls, uvidím Mám nový soubor s názvem souboru. A když jsem ho otevřete, bude to mít přesně to, co kočky dal na příkazovém řádku. Takže teď když to udělám znovu, pak to bude přesměrovat výstup do souboru, a budu mít stejný přesně to. Takže technicky to zcela nedbala toho, co jsme měli. A uvidíme, jestli změním dict, jsem vytáhl psa. Nyní, když jsme se kočka dict do souboru znovu, budeme mít novou verzi s pes odstraněny. Tak to úplně potlačí jej. Místo toho, pokud budeme používat >>, bude to připojit soubor. Nyní, otevírání souboru, vidíme, máme jen samé tištěné dvakrát protože tam byl jednou, pak jsme připojena k originálu. Tak to je to, co> a >> dělat. Má další se zeptat - to není to zeptat. Druhý, který máme, je <, která, pokud> přesměruje standardní výstup, ty 2>, že je přesměrování standardní chyba. Takže pokud se něco na standardní chybový výstup, dopadlo by to dát do txt2. Povšimněme si ale, jestli jsem to 2>, pak je to stále tisku Ahoj, Rob! do příkazového řádku protože jsem jen přesměrování standardní chybu, já nejsem přesměrování normu ven. Standardní chyba a standardní výstup se liší. Pokud byste chtěli, aby skutečně psát na standardní chybový výstup, pak bych mohl změnit to být fprintf na stderr. Takže printf, ve výchozím nastavení, vytiskne na standardní výstup. Pokud chci tisknout na standardní chybový výstup ručně, pak musím použít fprintf a určit, co chci tisknout. Pokud místo toho jsem fprintf stdout, pak je to v podstatě rovnocenné printf. Ale fprintf na standardní chybový výstup. Takže teď, když jsem přesměrovat to na txt2, Hello, Rob! stále dostává vytištěn v příkazovém řádku protože je dostat vytištěny na standardní chybový výstup, a já jsem jen přesměrování standardní výstup. Kdybych teď přesměrovat standardní chyba, teď to nedostal tištěný, a txt2 bude Hello, Rob! Takže teď, můžete tisknout skutečné chyby na standardní chybový výstup a tisknout pravidelné zprávy na standardní výstup. A tak při spuštění programu, můžete jej spustit jako. / Hello tento typ s 2> tak, aby váš program bude běžet normálně, ale nějaké chybové zprávy, které dostanete, můžete zjistit později v protokolu chyb, tak chyby, a pak se podívejte později a vaše chyby soubor bude mít nějaké chyby, které se staly. Otázky? Poslední z nich je trubka, která si můžete myslet, jako že se normu z jednoho příkazu , která je tak standard dalšího příkazu. Příkladem je echo je příkazový řádek věc , který je jen tak echo, co jsem dal jako svůj argument. Nebudu dát uvozovky. Echo bla, bla, bla je jen tak tisknout bla, bla, bla. Předtím, když jsem řekl, že jsem musel dát Roba do txt souboru protože mohu jen přesměrovat soubory s příponou TXT, místo, / když jsem echo Rob a pak potrubí je do. / hello, bude to také udělat stejný typ věci. To je v současné době výstup tohoto příkazu, echo Rob, a používat to jako vstup pro. / hello. Můžete si ji představit jako první přesměrování echo Roba do souboru a poté zadejte do. / hello tohoto souboru, který byl právě na výstupu. Ale to má dočasný soubor z obrázku. Otázky týkající se, že? Další otázka se bude týkat tohoto. Co potrubí můžete použít k vyhledání počet unikátních jmen v souboru s názvem names.txt? Příkazy budeme chtít použít tady jsou jedinečné, tak uniq, a pak wc. Můžete to udělat man uniq skutečně podívat na to, co dělá, že, a to jen tak filtruje sousedící shodné řádky od vstupu. A člověk wc bude tisknout znak nového řádku, slovo, a počet bajtů pro každý soubor. A poslední budeme chtít použít je něco, který se bude jen trochu řádky souboru txt. Pokud udělám nějaký soubor txt, names.txt, a to Rob, Tommy, Joseph, Tommy, Joseph, RJ, Rob, to, co chci udělat je zjistit počet unikátních jmen v tomto souboru. Takže to, co by měla být odpověď? >> [Student] 4. Jo >>. Mělo by být 4 od Roba, Tommy, Josef, RJ jsou pouze jedinečné názvy v tomto souboru. Prvním krokem, když jsem prostě počet slov na names.txt, je to vlastně říká mi všechno. To je vlastně tisk - Pojďme se podívat, man wc - nové řádky, slova, a byte počet. Kdybych jen o vedení, pak jsem si jen udělat wc-l names.txt. Tak to je krok 1. Ale já nechci, aby wc-l names.txt protože names.txt jen obsahuje všechny názvy, a chci odfiltrovat žádné non-jedinečné ty. Takže pokud jsem to Uniq names.txt, že není úplně dej mi to, co chci protože duplikované názvy jsou stále tam. Jak to? Proč se uniq není dělat to, co chci? [Student] duplicity nejsou [neslyšitelné] >> Jo. Nezapomeňte si man stránku pro Uniq říká filtruje sousedící shodné řádky. Jsou spolu nesousedí, takže to nebude filtrovat. Kdybych třídit je první, sort names.txt bude dát všechny duplicitní řádky dohromady. Takže teď trochu names.txt je. Budu chtít použít, že jako vstup do uniq, který je | uniq. To mi dává Josefa, RJ, Rob, Tommy, a chci jej použít jako vstup do wc-l, které se bude dát mi 4. Stejně jako tady se píše, co by mohlo potrubí použít? Můžete si udělat hodně věcí, jako pomocí série příkazů kde použít výstup z jednoho příkazu jako vstup do dalšího příkazu. Můžete si udělat hodně věcí, hodně chytrých věcí. Otázky? Dobře. To je to pro potrubí a přesměrování. Teď půjdeme na skutečné věci, kódování stuff. Uvnitř tohoto PDF, uvidíte tento příkaz, a budete chtít spustit tento příkaz ve vašem přístroji. wget je příkaz pro jen dostat něco z internetu, v podstatě, tak wget a to URL. Pokud jste šel na tuto adresu URL v prohlížeči, bylo by to stáhnout tento soubor. Jen jsem kliknul na to, tak to stáhli soubor pro mě. Ale psaní wget této věci uvnitř terminálu se právě chystá stáhnout do svého terminálu. Mám section5.zip, a budete chtít rozbalit section5.zip, které se bude dát vám složku s názvem section5, která bude mít všechny soubory se budeme používat dnes uvnitř ní. Jak souborů tyto programy "názvy napovídají, že jsou trochu buggy, Takže vaším úkolem je přijít na to, proč používat gdb. Má všechny nechat stáhnout / vědět, jak se dostat je ke stažení do jejich zařízení? Dobře. Běh ./buggy1, bude říkat Segmentation fault (core dumpingové), který pokaždé, když se segfault, je to špatná věc. Za jakých okolností byste si segfault? [Student] Nepřímý odkaz nulový ukazatel. Jo >>. Takže to je jeden příklad. Nepřímý odkaz nulový ukazatel Budeš se dostat segfault. Co segfault znamená je, že se dotknete paměti byste neměli dotýkat. Takže dereferencing nulový ukazatel se dotýká adresu 0, av podstatě všechny počítače v současné době říci, že adresa 0 je paměť, kterou jste neměli dotýkat. Takže to je důvod, proč dereferencing nulový ukazatel výsledků v segfault. Pokud se vám stalo, není inicializovat ukazatel, pak to má na odpadky hodnotu, a tak při pokusu o dereference to, se vší pravděpodobností jste se dotknete paměti že je uprostřed ničeho. Pokud se vám stalo, štěstí a odpadky hodnota stalo poukázat na někde na frontě, nebo tak něco, pak, když dereference, že ukazatel, který jste nebyl inicializován, nic pokazit. Ale pokud je to ukázal na, řekněme, někde mezi zásobníku a haldy, nebo je to ukazuje jen někde, že nebyl použit ani programu ještě, pak jste se dotknete paměť, neměli byste se dotýkat a segfault. Pokud napsat rekurzivní funkci, a to recurses příliš mnohokrát a váš stack roste příliš velký a zásobník srazí do věcí že by neměl být kolize s, jste dotýká paměti byste neměli dotýkat, takže segfault. To je to, co segfault je. Je to také ze stejného důvodu, že pokud máte řetězec ve tvaru - Pojďme se vrátit k předchozímu programu. Ve hello.c--já jen tak, aby se něco jiného. char * s = "hello world!"; Mám-li použít * s = něco nebo s [0] = 'X'; tak, aby ahoj,. / hello, proč se to segfault? Proč se to segfault? Co byste očekávat, že se stane? Pokud jsem printf ("% s \ n", s); co byste očekávat, že bude tisknout? [Student] X ahoj. Jo >>. Problém je, že při deklarování řetězec, jako je tento, s je ukazatel, který půjde na stack, a co s ukazuje na to je řetězec, který je obsažen v nepřepisovatelné paměti. Takže jen podle jména, jen pro čtení paměti, měli byste mít představu , že pokud se pokusíte změnit to, co je v read-only paměti, děláte něco, co by neměl dělat s pamětí a segfault. To je vlastně velký rozdíl mezi char * s a char s []. Takže char s [], nyní tento řetězec se bude kladen na zásobníku, a zásobník je jen pro čtení, což znamená, že by to mělo fungovat naprosto v pořádku. A to dělá. Pamatujte si, že když jsem si char * s = "hello world!", S sám je na zásobníku ale s body někam jinam, a že někde jinde se stane, že jen pro čtení. Ale char s [] je prostě něco, na zásobníku. Takže to je další příklad segfault děje. Viděli jsme, že ./buggy1 vyústila v segfault. Teoreticky, neměli byste se podívat na buggy1.c okamžitě. Místo toho se podíváme na to přes gdb. Všimněte si, že když se dostanete Segmentation fault (core dumpingové), si stáhni soubor sem volal jádro. Pokud bychom ls-l, uvidíme, že jádro je obvykle docela velký soubor. To je počet bajtů v souboru, takže to vypadá, že je to 250-něco kilobajtů. Důvodem je, že to, co core dump je ve skutečnosti je, když váš program havaruje, stav paměti programu jen dostane zkopírovat a vložit do tohoto souboru. To dostane vyhozen do tohoto souboru. Tento program, zatímco byl spuštěn, se stalo mít využití paměti asi 250 kb, a tak to je to, co dostal kopačky do tohoto souboru. Nyní se můžete podívat na tomto souboru, pokud budeme dělat gdb buggy1 jádro. Můžeme prostě gdb buggy1, a že bude jen spuštění gdb pravidelně, pomocí buggy1 jako jeho vstupní soubor. Ale pokud si gdb buggy1 jádro, pak je to konkrétně jde nastartovat gdb při pohledu na tento základní soubor. A říkáte buggy1 znamená gdb ví, že jádro souboru pochází z buggy1 programu. Takže gdb buggy1 jádro bude okamžitě přinést nám kde program stalo ukončit. Vidíme zde Program ukončen signálem 11, Segmentation fault. Jsme se ocitli na řadu shromáždění, které pravděpodobně není příliš užitečné. Ale pokud zadáte BT nebo backtrace, že to bude funkce který nám dává seznam našich aktuálních zásobníku snímků. Tak backtrace. Vypadá to, že máme jen dvě stack rámy. První z nich je naše hlavní rámec fronty, a druhý je zásobník rám pro tuto funkci, která se stane, že je v, který vypadá jako máme pouze montážní kód. Tak pojďme zpět do naší hlavní funkce, a to, že můžeme udělat rámeček 1, a myslím, že můžeme také udělat dolů, ale skoro nikdy to dolů - nebo nahoru. Jo. Nahoru a dolů. Až vám přináší jednu zásobníku snímek, dolů se dostanete dolů zásobníku rám. Mám ve zvyku nikdy použít. Jen jsem konkrétně říct, rám 1, která je jít do rámu s označením 1. Rám 1 bude nás zavede do hlavního zásobníku rámu, a říká, že tady na řádek kódu jsme náhodou být. Pokud bychom chtěli ještě pár řádků kódu, můžeme říci seznamu, a že se to nám všem řádky kódu kolem něj. Linka jsme segfaulted na to 6: if (strcmp ("CS50 skály", argv [1]) == 0). Pokud to není zřejmé ještě, můžete si jej rovnou odtud jen tím, že myslí, proč je segfaulted. Ale můžeme si to ještě o krok dále a říci, "Proč by argv [1] segfault?" Pojďme tisk argv [1], a vypadá to, že je to 0x0, který je nulový ukazatel. Jsme strcmping CS50 skály a NULL, a tak že to bude segfault. A proč je argv [1] null? [Student] Protože jsme neměli dát žádné argumenty příkazového řádku. Jo. Nechtěli jsme dát žádné argumenty příkazového řádku. Takže ./buggy1 pouze bude mít argv [0] bude ./buggy1. Nebude to mít argv [1], tak, že to bude segfault. Ale pokud, místo toho jsem dělat jen CS50, že to bude říkat Získáte D protože to je to, co má udělat. Při pohledu na buggy1.c, to má tisknout "Dostanete D" - Pokud argv [1] není "CS50 skály", "Dostanete D", jinak "Zde získáte!" Takže pokud chceme, musíme to porovnat jako pravdivé, , což znamená, že je v porovnání s 0. Takže argv [1] musí být "CS50 skály". Pokud chcete udělat, že na příkazovém řádku, budete muset použít \ uniknout prostor. Takže CS50 \ skály a dostanete A! Pokud nechcete dělat zpětné lomítko, proč to nefunguje? [Student] Je to dva různé argumenty. Jo >>. Argv [1] bude CS50, a argv [2] bude skály. Dobře. Nyní ./buggy2 bude segfault znovu. Místo otevření se své hlavní souboru, budeme jen otevřít buggy2 přímo, tak gdb buggy2. Nyní, když jsme jen spustit náš program, pak to bude říkat Program přijímaného signálu SIGSEGV, který je segfault signál, a to je místo, kde se stalo stalo. Při pohledu na naše backtrace, vidíme, že jsme byli ve funkci oh_no, který byl povolán funkce Dinky, který byl nazýván pomocí funkce Binkym, který byl povolán hlavní. Můžeme také vidět argumenty těchto funkcí. Argument bezvýznamné a Binky byla 1. Pokud bychom seznamu funkci oh_no, vidíme, že oh_no je jen to, char ** S = NULL; * S = "BOOM"; Proč by to selhání? [Student] Nemůžete dereference null ukazatel? Jo >>. To je jen říká s je NULL, bez ohledu na to, pokud se to stane, že je char **, které, v závislosti na tom, jak interpretovat to, mohlo by to být ukazatel na ukazatel na řetězec nebo pole řetězců. Je to s je NULL, takže * s je dereferencing nulový ukazatel, a tak to bude pád. To je jeden z nejrychlejších způsobů, jak můžete případně segfault. Je to prostě prohlašuje, že nulový ukazatel a okamžitě segfaulting. To je to, co oh_no dělá. Pokud půjdeme do jednoho snímku, pak budeme dostat do funkce, která je volána oh_no. Musím to udělat, aby se. Pokud nezadáte příkaz a vy jednoduše stisknout Enter znovu, to bude jen opakovat předchozí příkaz, který jste spustili. Jsme v rámu 1. Výpis tohoto rámu, zde vidíme, je naše funkce. Můžete hit seznam znovu, nebo si můžete udělat seznam 20 a bude obsahovat více. Funkce dinky říká, že pokud je i 1, pak přejděte na funkci oh_no, jiný jít do slinky funkci. A my víme, i je 1, protože jsme se ocitli na tady že dinky byla volána s argumentem 1. Nebo můžete prostě vytisknout i, a to bude říkat i je 1. Jsme v současné době v Dinky, a jdeme-li do dalšího snímku, víme, že skončíš v Binkym. Up. Nyní jsme v Binkym. Výpis tuto funkci - seznam z před půl mě přerušil - Začalo to jako když jsem je 0, pak budeme říkat, že oh_no, jinak volejte hezoučký. Víme, že jsem byl 1, tak to je voláno hezoučký. A teď jsme zpátky v hlavním, a hlavní je jen bude int i = rand ()% 3; To je jen tak, aby vám náhodné číslo, které je buď 0, 1, nebo 2. Bude to říkat Binky s tímto číslem, a to vrátí 0. Při pohledu na to, jen procházky programu ručně bez spuštění okamžitě, byste nastavit bod zlomu na hlavní, což znamená, že když jsme se spustit program váš program běží až dokud nenarazí na bod přerušení. Takže spuštění programu, bude to běžet a pak to bude hit hlavní funkce a zastavit běh. Teď jsme uvnitř hlavní, a krok, nebo vedle se chystá přivést nás na další řádek kódu. Můžete to udělat krok nebo další. Bít další, teď jsem byl nastaven na rand ()% 3, a tak můžeme vytisknout hodnotu i, a to bude říkat i je 1. Teď to záleží, zda budeme používat následující nebo krok. Myslím, že by na tom záleželo v předchozím, ale budeme chtít použít další. Pokud budeme používat krok, jsme krok do funkce, což znamená, že podívat se na skutečné věci , co se děje uvnitř Binkym. Pokud budeme používat dál, pak to znamená jít přes funkci a prostě jít na další řádek kódu v naší hlavní funkce. Právě zde na této trati, byl jsem na místo, kde se říká rand ()% 3; kdybych to udělal krok, bylo by to jít do provádění rand a podívat se na to, co se tam děje, a já jsem mohl šlápnout pomocí funkce rand. Ale já se nestarám o funkci rand. Já jen chci jít na další řádek kódu v hlavní, tak jsem použít další. Ale teď jsem to stará o Binky funkci, takže chci, aby krok do toho. Teď jsem v Binkym. První řádek kódu je říct if (i == 0), jsem o krok, vidíme, skončí na Dinky. Pokud jsme seznam věcí, vidíme, že četl je i = 0. i není rovno 0, tak to šlo do jiného stavu, která se bude volat Dinky (i). Můžete se zmást. Pokud stačí se podívat na těchto tratích přímo, možná si myslíte, že if (i == 0), v pořádku, pak jsem udělal krok, a teď jsem na Dinky (i), si může myslet, že musí znamenat i = 0, nebo tak něco. Ne Jde o to, že ví, že může přilepit přímo na řádek Dinky (i). Vzhledem k tomu, i není 0, je dalším krokem nebude končit na ostatnímu. Else není položka, že to bude zastavit na. Je to prostě jít na další řádek může skutečně spustit, což je hezoučký (i). Vstup do Dinky (i), uvidíme, jestli (i == 1). Víme, i = 1, takže když jsme krok, víme, že skončíme v oh_no protože i = 1 volá funkci oh_no, které můžete vstoupit do, který se bude nastavení char ** s = NULL a okamžitě "BOOM". A pak vlastně se dívat na provádění buggy2, to, i se jen na to, náhodné číslo - 0, 1, nebo 2 - volání Binky, která pokud je i 0 volá oh_no, jinde to volá hezoučký, který přijde sem. Pokud i je 1, výzva oh_no, jinak volejte Slinky, který přijde sem, pokud je i 2, volejte oh_no. Nevím ani, že existuje způsob, jak - Má někdo vidí způsob, jak dělat to program, který nebude segfault? Protože pokud jsem něco chybí, pokud je i 0, budete okamžitě segfault, jinak jdete do funkce, která, pokud i je 1 jste segfault, jinak jdete do funkce, pokud je i 2 si segfault. Takže bez ohledu na to, co děláte, budete segfault. Myslím, že jeden způsob, jak se stanoví, že by místo toho dělal char ** S = NULL, můžete malloc prostor pro tento řetězec. Mohli bychom udělat malloc (sizeof) - sizeof co? [Student] (char) * 5? >> Znamená to nezdá správné? Já jsem za předpokladu, že to bude fungovat, pokud jsem vlastně běžel, ale to není to, co jsem hledal. Podívejte se na typ s. Dodejme, int *, takže int * x. Já bych to malloc (sizeof (int)). Nebo když jsem chtěl pole 5, by se mi nelíbí (sizeof (int) * 5); Co když mám int **? Co bych malloc? [Student] Velikost ukazatele. Jo >>. (Sizeof (int *)); Stejná věc se tady. Chci (sizeof (char *)); To bude přidělit prostor pro ukazatel, který ukazuje na "boom". Nepotřebuji přidělit místo pro "BOOM" sám protože to je v podstatě ekvivalentní tomu, co jsem řekl předtím char * x = "BOOM". "BOOM" již existuje. Stává se, že existují v read-only oblasti paměti. Ale to již existuje, což znamená, že tento řádek kódu, jestliže S je char **, pak * s je char * a vy nastavení tohoto char * poukázat na "BOOM". Pokud bych chtěl kopírovat "boom" do s, pak budu muset přidělit prostor pro s. Udělám * s = malloc (sizeof (char) * 5); Proč 5? Proč ne 4? Vypadá to, že "boom" je 4 znaky. >> [Student] null znak. Jo. Všechny vaše řetězců budou potřebovat null charakter. Teď můžu udělat něco jako strcat - Jaká je funkce pro kopírování řetězce? [Student] cpy? >> Strcpy. muž strcpy. Takže strcpy nebo strncpy. strncpy je trochu bezpečnější, protože můžete přesně specifikovat, kolik znaků, ale tady to nevadí, protože víme. Takže strcpy a podívat se do argumentů. První argument je náš cíl. Druhý argument je naším zdrojem. Budeme kopírovat do našeho cílového * S ukazatel "BOOM". Proč by si to chcete udělat s strcpy místo právě to, co jsme měli předtím z * s = "BOOM"? K dispozici je důvod, proč budete chtít udělat, ale co je to důvod? [Student] Chcete-li něco změnit v "BOOM". Jo >>. Teď můžu udělat něco jako s [0] = 'X'; protože s body do haldy a že prostor na haldě, která je ukazuje na je ukazatel na další místa na haldě, která je ukládání "boom". Takže to kopie "boom" je uložen v haldě. Tam jsou technicky dvě kopie "boom" v našem programu. Tam je první, který se právě dán touto "boom" řetězcová konstanta, a druhou kopii "boom", strcpy vytvořil kopii "boom". Ale kopie "boom" je uložen na haldě, a haldy máte možnost změnit. Halda je jen pro čtení, tak to znamená, že s [0] se chystá nechat změnit hodnotu "boom". Bude to vám umožní změnit tyto znaky. Otázky? Dobře. Přesun na buggy3, pojďme gdb buggy3. Jsme jen spustit a vidíme, se segfault. Pokud bychom backtrace, tam jsou jen dvě funkce. Pokud bychom šli do naší hlavní funkce, vidíme, že segfaulted na tomto řádku. Takže jen při pohledu na tento řádek, for (int řádek = 0; fgets tohle není rovný NULL; linka + +). Naše předchozí snímek byl nazýván _IO_fgets. Uvidíte, že šarže vestavěných funkcí C, že když se dostanete segfault, bude opravdu záhadné názvy funkcí podobné _IO_fgets. Ale že se to souvisí s tímto fgets výzvy. Někde sem dovnitř, jsme segfaulting. Podíváme-li se na argumenty fgets, můžeme tisknout vyrovnávací paměti. Pojďme vytisknout jako - Oh, ne. Tisk nebude fungovat přesně tak, jak chci, aby to. Pojďme se podívat na aktuální program. Buffer je pole znaků. Je to postava pole 128 znaků. Takže když říkám tisk buffer, že to bude tisknout ty 128 znaků, která Myslím, že je to, co se očekává. Co jsem hledal, je vytisknout adresu vyrovnávací paměti, ale to není opravdu mi hodně. Takže když jsem se náhodou říct, tady x buffer, ukazuje mi 0xbffff090, které, pokud si pamatujete z dříve nebo nějakého bodu, Oxbffff inklinuje být stack-ish region. Zásobník má tendenci začít někde těsně pod 0xc000. Jen tím, že vidí tuto adresu, já vím, že vyrovnávací paměti se děje na zásobníku. Restartování můj program, spusťte, up, mírnění jsme viděli, bylo tuto posloupnost znaků které jsou do značné míry smysl. Pak tisk souboru, co to soubor vypadá? [Student] Null. Jo >>. Soubor je z * typ souboru, takže je to ukazatel, a hodnota tohoto ukazatele je nulový. Takže fgets se pokusí číst z tohoto ukazatele v nepřímým způsobem, ale aby se přístup k tomuto ukazatel, že má k dereference to. Nebo, aby se přístup, co by mělo být směřující, to dereferences IT. Takže to dereferencing nulový ukazatel, a to segfault chyb. Mohl jsem znovu ho tam. Jestliže zrušíme v našem hlavním bodě a spustit, první řádek kódu je char * filename = "nonexistent.txt"; To by mělo docela velký náznak toho, proč tento program selže. Psaní vedle mě přivádí na další řádek, kde jsem otevřít tento soubor, a pak jsem okamžitě dostat do naší linie, kde kdysi jsem zasáhla další, bude to segfault. Má někdo chtěl vyhodit důvod, proč bychom mohli být segfaulting? [Student] Soubor neexistuje. Jo >>. To má být náznak že pokud jste otevření souboru je nutné zkontrolovat, zda soubor skutečně existuje. Tak tady, "nonexistent.txt"; Když jsme fopen souboru pro čtení, pak jsme třeba říkat if (soubor == NULL) a říkat printf ("Soubor neexistuje!" nebo - ještě lépe - filename); return 1; Takže teď jsme zkontrolovat, jestli je to NULL než ve skutečnosti pokračování a snaží se číst z tohoto souboru. Můžeme předělat to prostě vidět, že to funguje. Měl jsem v úmyslu zahrnout nový řádek. Takže teď nonexistent.txt neexistuje. Vždy byste měli zkontrolovat pro tento druh věci. Vždy byste měli zkontrolovat, zda fopen vrátí NULL. Vždy byste měli zkontrolovat, aby se ujistil, že malloc nevrátí NULL, jinak byste segfault. Teď buggy4.c. Běh. Hádám, že je to čeká na vstup nebo případně nekonečné smyčky. Ano, je to nekonečné smyčky. Tak buggy4. Vypadá to, že jsme nekonečné smyčky. Můžeme rozdělit na hlavní, spusťte náš program. V gdb, tak dlouho, jak zkratka použít je jednoznačná nebo speciální zkratky, které poskytují pro vás, pak můžete použít n k použití příště místo toho, aby musel psát v příštím celou cestu. A teď, když jsem udeřil n jednou, mohu jen stiskněte Enter dál další místo toho, aby museli zasáhnout n Enter, n Enter, n Enter. Vypadá to, že jsem v nějaké smyčce for, že je nastavení pole [i] na 0. Vypadá to, že jsem nikdy vypadla z toho pro smyčky. Kdybych tisku i, tak jsem je 2, pak půjdu dál. Budu tisknout i, i je 3, pak půjdu dál. Budu tisknout i a i je 3. Další, tisk i, i je 4. Vlastně, tisk sizeof (pole), takže velikost pole je 20. Ale vypadá to, že tam je nějaký zvláštní gdb příkaz pro přechod až se něco stane. Je to jako nastavení podmínku na hodnotě proměnné. Ale já nevím, co to je. Takže pokud budeme pokračovat - Co jsi říkal? Co jste vychovávat? [Student] Má zobrazí i přidat - >> Jo. Takže zobrazí i vám může pomoci. Pokud bychom jen zobrazit i, bude to dát sem, co hodnota i je takže nemám ji vytisknout pokaždé. Pokud budeme jen dál vedle, vidíme 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5. Něco se děje strašně špatně, a já se nastaví na 0. Při pohledu na buggy4.c, vidíme všechno, co se stane, je int pole [5]; for (i = 0; i <= sizeof (array); i + +) array [i] = 0; Co vidíme, že je to špatně? Jako náznak, když jsem dělal gdb buggy4 - pojďme rozbít hlavní, run - Já jsem print sizeof (pole) jen proto, aby viděli, co je stav, kdy bych si měl konečně vypuknout. Kde to jsem? Jsem utíkal? Jsem nepřiznal ještě. Takže tisk sizeof (pole), a to 20, kterého se očekává, protože moje pole je velikosti 5 a je to z 5 celých čísel, takže celá věc by měla být 5 * sizeof (int) bytů, pokud sizeof (int) tendence k 4. Takže sizeof (pole) je 20. Co by to mohlo být? [Student] děleno sizeof (int). >> Jo, / sizeof (int). Vypadá to, že je tu ještě jeden problém. Myslím, že by to mělo být jen < protože je to docela hodně vždy > [Bowden] Ano. Když jdeme po skončení našeho pole, nějak, že prostor, který jsme přepsání je přepsání hodnoty i. A tak když se podíváme do buggy4, rozbít hlavní, běh, Pojďme vytisknout adresu i. Vypadá to, že to bffff124. Nyní pojďme vytisknout adresu pole [0]. 110. Co [1]? 114. [2], 118. 11c, 120. array [5] je bfff124. Takže pole [5] má stejnou adresu jako já, což znamená, že pole [5], je i. Pokud mají stejnou adresu, jsou jedno a totéž. Takže když jsme se pole [5] na 0, kterou vytváříme i na 0. A pokud si myslíte, že o tom, pokud jde o zásobníku, int i je deklarován jako první, což znamená, že jsem se dostane určitý prostor na zásobníku. Pak array [5] je přiděleno, tak pak 20 bytů jsou přidělovány na zásobníku. Takže i dostane přiděleno první, pak těchto 20 bytů si přiděleny. Tak jsem se děje přímo před pole, a kvůli tomu, jak, jak jsem řekl minulý týden, pokud je to technicky stack roste dolů, když index do pole, jsou zaručeny jsme, že 0. pozice v poli vždy se stane před prvním místě v poli. To je druh, jak jsem nakreslil minulý týden. Všimněte si, že ve spodní části máme adresu 0 a na vrcholu jsme adresu Max. Zásobník je vždy roste dolů. Řekněme, že jsme se rozdělit i. Jsme alokovat integer i, což znamená, řekněme, že se tu celé číslo i dostane přiděleno. Pak jsme přidělit naší nabídku 5 celých čísel, což znamená, že pod tím, protože zásobník roste dolů, dostat přiděleno těch 5 celá čísla. Ale protože, jak pole pracovat, to zaručuje, že jsme první pozice v poli má vždy adresu menší než druhý věc v poli. Takže array poloha 0 musí vždy stát první v paměti, vzhledem k tomu, pole pozici 1 má stát poté, co a pole pozice 2 se stane po tom, což znamená, že pole v poloze 0 by se stalo, někde tady dole, Pole pozice 1 by se stalo, že výše protože pohybující se rozumí vyšší adresy, protože maximální adresa je tady. Takže pole [0] se zde, array [1] tady, array [2] tady, array [3] tady. Všimněte si, jak před tím, než přidělí celé číslo i po celou cestu až sem, jak jsme se přesunout dále a dále do našeho pole, jsme stále blíž a blíž k našemu celé číslo i. To jen tak se stane, že pole [5], což je o jednu pozici nad naše pole, je přesně tam, kde celé číslo i stalo které mají být přiděleny. Tak to je místo, kde jsme se náhodou bít prostor na zásobníku , která byla přidělena pro celočíselné i, a my jsme nastavení, které se 0. To je, jak to funguje. Otázky? Jo. [Student] To nevadí. Dobře. [Student] Jak se vyhnout těmto druh chyby? Tyto druh chyby? Nepoužívejte C jako programovací jazyk. Používejte jazyk, který má pole meze kontroly. Tak dlouho, jak jste opatrní, stačí, aby se zabránilo jít za hranice svého pole. [Student] Tak tady, když jsme šli kolem hranice svého pole - [Bowden] To je místo, kde se věci začnou špatně. >> [Student] Oh, dobře. Tak dlouho, jak si pobyt v paměti přidělené pro pole, že jsi v pořádku. Ale C dělá žádnou kontrolu chyb. Pokud to udělám pole [1000], bude to s radostí jen změnit ať se stane cokoli - To vede k začátku pole, pak jde 1000 postavení po a nastaví na hodnotu 0. To nedělá žádnou kontrolu, že oh, to není ve skutečnosti mít 1000 věcí v něm. 1000 je daleko za to, co jsem měla být změna, vzhledem k tomu, Java nebo něco dostanete nabídku z indexu bounds nebo index out of bounds výjimky. To je důvod, proč mnoho jazycích vyšší úrovně mají tyto věci kde když jdete za hranice pole, se vám to nepodaří takže není možné měnit věci z pod vámi a pak se věci jít mnohem horší, než jen dostat výjimku říká, že jste šel za konec pole. [Student] A tak jsme měli pouze změnil <= jen > [Bowden] Jo. To by mělo být > [Student] Právo. Další otázky? Dobře. [Student] Mám dotaz. Jo >>. [Student] Jaký je skutečný pole proměnná? [Bowden] Jako co je pole? Array sám je symbolem. Je to právě adresa začátku 20 bajtů, které jsme odkazujících. Můžete si ji představit jako ukazatel, ale je to konstanta ukazatel. Jakmile se věci shromážděny, proměnná pole již neexistuje. [Student] Tak jak to zjistit velikost pole? Velikost pole se odkazuje na velikost tohoto bloku, který tento symbol odkazuje. Když jsem udělat něco jako printf ("% p \ n", pole); pojďme spustit. Co jsem právě udělal špatně? Array "array" prohlásil zde. Oh, tady. Řinčet je chytrý, a to se stane si, že jsem prohlásil pole jako 5 elementů ale já jsem indexování do polohy 1000. To může udělat, protože to jsou jen konstanty. To může jít jen tak daleko, všiml si, že budu mimo hranice pole. Povšimněme si ale předtím, když jsme měli i být nesprávné, nelze přesně určit, kolik hodnot bych si mohl vzít na, tak to nemůže stanovit, že jsem šel za konec pole. To je jen zvonění je chytrý. Ale teď dělat buggy4. Takže co jiného dělám špatně? Implicitně prohlašuje knihovní funkce "printf". Budu chtít, aby # include . Dobře. Nyní běží buggy4. Tisk hodnotu pole jako já zde, potisk jako ukazatel postery něco, co vypadá takhle - bfb8805c - což je asi adresa že je v zásobníku-ish regionu. Pole sám je jako ukazatel, ale není to skutečný ukazatel, protože běžný ukazatel můžeme změnit. Pole je jen nějaká konstanta. V 20 bloků paměti kdo na adrese 0xbfb8805c. Takže bfb8805c prostřednictvím této adrese +20- nebo myslím -20 - je všechny paměti přidělené pro toto pole. Array, je proměnná sama není uloženo kdekoliv. Když jste sestavování, kompilátor - Hand Wave na to - ale kompilátor bude jen používat, pokud ví, pole se. Ví, kde toto pole začíná, a tak to může vždy jen dělat věci, pokud jde o kompenzace od tohoto počátku. To nepotřebuje proměnnou sám reprezentovat pole. Ale když jsem něco takového int * p = pole; nyní p je ukazatel, který ukazuje na tomto poli, a nyní p vlastně neexistuje v zásobníku. Jsem volná změnit ks. Mohu p = malloc. Tak to původně zaměřila na pole, teď to ukazuje na nějaký prostor na haldě. Nemůžu pole = malloc. Pokud zvonění je chytrý, bude to na mě křičet hned bat. Vlastně, jsem si docela jistý, gcc by to taky. Takže typ pole "int [5]" není převoditelná. Nemůžete přiřadit něco typu pole protože pole je jen konstantní. Je to symbol, který odkazuje tato 20 bytů. Nemůžu to změnit. [Student] A, kde je velikost pole uložena? [Bowden] To není uloženo kdekoliv. Je to, když je to kompilaci. Takže, kde je velikost pole uloženy? Můžete použít pouze sizeof (pole) uvnitř funkce, která pole je deklarována sám. Takže když udělám nějakou funkci, foo, a já (int array []) printf ("% d \ n", sizeof (pole)); a pak sem zavolám foo (array); uvnitř této funkce - pojďme jej spustit. To je zvonění je chytrý znovu. To mi říká, že sizeof na pole parametru funkce vrátí velikost "int * '. To by bylo chybou, pokud to není to, co jsem chtěl aby se to stalo. Pojďme skutečně vypnout Werror. Varování. Varování jsou v pořádku. To bude ještě sestavit tak dlouho, jak to má varování. . / A.out bude tisknout 4. Varování, která byla generována je jasným ukazatelem toho, co se stalo. Tento int pole je jen tak tisknout sizeof (int *). I když jsem dal nabídku [5] tady, je to stále jen tak tisknout sizeof (int *). Takže jakmile předáte jej do funkce, rozdíl mezi poli a ukazatele neexistuje. To se stane, že pole, které bylo deklarováno na zásobníku, ale jakmile jsme se projít tuto hodnotu, že 0xbf bla, bla, bla do této funkce, pak tento ukazatel ukazuje na daném poli v zásobníku. Takže to znamená, že sizeof platí pouze ve funkci, která pole byla prohlášena, což znamená, že když se sestavování tuto funkci, Při zvonění prochází této funkce, to vidí pole je int pole o velikosti 5. Takže pak to vidí sizeof (pole). No, to je 20. To je vlastně, jak sizeof funguje v podstatě téměř ve všech případech. Sizeof není funkce, je to operátor. Nemusíte volat sizeof funkci. Sizeof (int), bude kompilátor jen překládat které až 4. Máš to? Dobře. [Student] Tak v čem je rozdíl mezi sizeof (pole) v hlavní a v foo? To je proto, že říkáte, že sizeof (pole), což je z * typ int, vzhledem k tomu, pole tady dole nemá * typu int, je to int pole. [Student] Takže pokud jste měli parametr v poli [] namísto int * pole, by znamenalo, že byste mohli ještě změnit pole, protože teď je to ukazatel? [Bowden] Like this? >> [Student] Jo. Můžete změnit pole ve funkci teď? [Bowden] Dalo by se změnit pole v obou případech. V obou těchto případech si budete moci říct, array [4] = 0. [Student] Ale můžete si pole přejděte na něco jiného? [Bowden] Oh. Jo. V obou případech - >> [studentka] Jo. [Bowden] Rozdíl mezi pole [] a int * pole, tam není nikdo. Můžete také získat nějaké vícerozměrné pole zde pro některé pohodlné syntaxe, ale je to stále jen ukazatel. To znamená, že jsem si může dělat, pole = malloc (sizeof (int)), a nyní ukazují někam jinam. Ale stejně jako, jak to funguje navždy a vždy, Změnou tohoto pole tím, že ho upozornit na něco jiného nemění toto pole tady dole, protože je to kopie argumentu, to není ukazatel na tento argument. A skutečně, stejně jako další znamení, že je to přesně to samé - jsme již viděli, co tiskařský pole - co když tiskneme adresu pole nebo adresu adresu pole buď z nich? Pojďme ignorovat tento jeden. Dobře. To je v pořádku. Je to nyní běží. / A.out. Potisk pole, pak tisk adresu pole, jsou jedno a totéž. Array prostě neexistuje. To ví, kdy tisknete pole, tisknete na symbol, který odkazuje na těch 20 bajtů. Tisk adresu pole, dobře, pole neexistuje. To nemá adresu, tak to prostě vytiskne adresu těchto 20 bajtů. Jakmile budete kompilovat dolů, jako v kompilované buggy4. / A.out, pole je neexistující. Ukazatele existují. Pole ne. Bloky paměti představující pole stále existují, ale proměnná pole a proměnné tohoto typu neexistují. Ti jsou jako z hlavních rozdílů mezi poli a ukazatele jsou, jakmile uděláte volání funkce, není tam žádný rozdíl. Ale uvnitř funkce, které pole má prohlášení, sizeof pracuje odlišně protože tisknete velikost bloků namísto velikosti typu, a můžete jej změnit, protože je to symbol. Tisk věc a adresu věci vytiskne stejnou věc. A to je docela hodně to. [Student] Mohl byste říci, že ještě jednou? Možná jsem něco uniklo. Tisk pole a adresa pole vypíše to samé, vzhledem k tomu, pokud tisknete ukazatel oproti adresu ukazatele, jedna věc vytiskne adresu toho, co jste ukázal, jiný vytiskne adresu ukazatelem na zásobníku. Můžete změnit ukazatel, nemůžete změnit pole symbol. A sizeof ukazatel bude tisknout velikost tohoto ukazatele typu. Takže int * p sizeof (p) se bude tisknout 4, ale int array [5] print sizeof (pole), bude k tisku 20. [Student] Tak int array [5] se vytiskne 20? Ano >>. Proto uvnitř buggy4 když to bylo sizeof (pole) to bylo dělal i <20, což není to, co jsme chtěli. Chceme i <5. >> [Student] Dobře. [Bowden] A pak, jakmile začnete procházet v funkcemi, kdybychom int * p = array; uvnitř této funkce, můžeme v podstatě použít p a pole v přesně stejným způsobem, kromě problému sizeof a měnící problém. Ale p [0] = 1, je stejný jako pořekadlo pole [0] = 1; A jakmile říkáme foo (array); nebo foo (p); uvnitř funkce foo, to je stejný hovor dvakrát. Není žádný rozdíl mezi těmito dvěma hovory. Všichni dobře na to? Dobře. Máme 10 minut. Budeme se snažit dostat se přes tento program Hacker Typer, tento web, který vyšel v loňském roce nebo tak něco. Je to prostě má být, jako píšete náhodně, a to vytiskne - Ať soubor se to stane, že v zásobníku se, jak to vypadá píšete. Vypadá to jako nějaký druh operačního systému kódu. To je to, co chceme realizovat. Měli byste mít binární spustitelný názvem hacker_typer který bere v jediném argumentu, soubor "hacker typu." Spuštění spustitelný by vyčistit obrazovku a pak vytisknout jeden znak z předaný v souboru pokaždé, když uživatel stiskne klávesu. Takže bez ohledu na klíč stisknutí, měl by vyhodit a místo toho vytiskne znak ze souboru že je argument. Budu docela hodně říci, co věci budeme potřebovat vědět, jsou. Ale chceme se podívat na knihovnu termios. Nikdy jsem používal tento knihovnu v celém mém životě, tak to má velmi minimální účely. Ale to bude knihovna můžeme použít vyhodit znak, který hit když píšete do standardu palců Takže hacker_typer.c, a budeme chtít, aby # include . Při pohledu na manuálové stránce pro termios - já hádat, že je to terminál OS nebo tak něco - Já nevím, jak to číst. Při pohledu na to, že říká, že i na tyto 2 soubory, takže budeme dělat, že. První věc, kterou jako první, chceme, aby se v jediném argumentu, který je soubor bychom měli otevřít. Tak co chci dělat? Jak mohu zkontrolovat, zda mám jediný argument? [Student] Pokud argc rovná ji. >> [Bowden] Jo. Takže if (argc = 2!) Printf ("Použití:% s [soubor otevřít]"). Takže teď, když spustím to bez udání druhý argument - oh, potřebuju novou linku - uvidíte, že říká použití:. / hacker_typer, a pak druhý argument by měl být soubor chci otevřít. Teď, co mám dělat? Chci číst z tohoto souboru. Jak jsem četl ze souboru? [Student] můžete otevřít jako první. Jo >>. Tak fopen. Co fopen vypadá? [Student] souboru. >> [Bowden] souboru bude argv [1]. [Student] A pak to, co chcete dělat s tím, aby - >> [Bowden] Jo. Takže pokud jste si vzpomenout, mohl by si udělat muž fopen, kde to bude const char * path, kde path je jméno souboru, const char * mode. Pokud jste náhodou nepamatujete, co je režim, pak se můžete podívat na režim. Uvnitř manuálových stránek, znak lomítko je to, co můžete použít k vyhledání věci. Tak jsem typ / režim vyhledávání pro režim. n a N jsou to, co můžete použít k procházení vyhledávání zápasů. Zde se říká, že body argumentů režimu na řetězec začíná s jedním z následujících posloupností. Takže r, Open textový soubor pro čtení. To je to, co chceme dělat. Pro čtení, a chci uložit, že. Věc bude FILE *. Teď, co chci dělat? Dejte mi chvilku. Dobře. Teď, co chci dělat? [Student] Zkontrolujte, zda je to NULL. >> [Bowden] Jo. Pokaždé, když otevřete soubor, ujistěte se, že jste úspěšně schopný otevřít. Teď chci udělat, aby termios věci tam, kde chci nejprve přečíst moje současné nastavení a zachránit ty, do něčeho, pak chci změnit své nastavení vyhodit libovolný znak, který jsem typ, a pak chci aktualizovat tato nastavení. A pak na konci programu, chci změnit zpět na své původní nastavení. Takže struct bude typových termios, a budu chtít dva ty. První z nich bude moje current_settings, a pak to bude moje hacker_settings. Za prvé, budu chtít uložit své aktuální nastavení, pak budu chtít aktualizovat hacker_settings, a pak jak na konci svého programu, chci se vrátit na aktuální nastavení. Takže uložení aktuální nastavení, tak, že funguje, my man termios. Vidíme, že máme tuto int tcsetattr, int tcgetattr. Minul jsem v termios struct svým ukazatel. Způsob, jakým to bude vypadat, je - I've už zapomněli, co funkce byla volána. Zkopírujte a vložte ji. Takže tcgetattr, pak chci předat v struct, že jsem uložení informací v, která se bude current_settings, a první argument je deskriptor souboru pro věc, kterou chci uložit atributy. Co popisovač souboru je je jako kdykoliv otevřete soubor, dostane popisovač souboru. Když jsem fopen argv [1], se dostane popisovač souboru, který jste odkazující pokaždé, když chcete číst nebo do ní zapisovat. To není deskriptor souboru chci používat zde. K dispozici jsou tři deskriptory souborů, které máte ve výchozím nastavení, které jsou standardní v, standardní výstup a standardní chyba. Ve výchozím nastavení, myslím, že je to standard v je 0, standardní výstup je 1 a směrodatná odchylka je 2. Takže to, co chci změnit nastavení? Chci změnit nastavení, když jsem trefil postavu, Chci, aby to hodit, že charakter pryč, místo tisku na obrazovku. Co potok - standard v, standardní výstup, nebo standardní chybou - reaguje na věci, když jsem typ na klávesnici? >> [Student] Standardní a. >> Jo. Tak jsem si buď udělat 0, nebo můžu udělat stdin. Začínám se current_settings z normy a. Teď chci aktualizovat tato nastavení, takže první budu kopírovat do hacker_settings, co moji current_settings jsou. A jak structs práce je, že bude jen kopírovat. To zkopíruje všechna pole, jak byste očekávali. Teď chci aktualizovat některé z polí. Při pohledu na termios, byste si přečíst přes hodně to jen aby viděl, co byste chtěli hledat, ale příznaky budete chtít hledat, jsou echo, tak ECHO znaky Echo vstupní. Nejprve chci nastavit - I've už zapomněli, co jsou pole. To je to, co struct vypadá. Takže vstupní režimy myslím, že chceme změnit. My se podíváme na řešení, aby se ujistil, že je to to, co chceme změnit. Chceme změnit lflag, aby se zabránilo nutnosti prohlédnout všechny tyto. Chceme změnit místní režimy. Budete muset přečíst tento celou věc pochopit, kde všechno patří že chceme změnit. Ale je to uvnitř místních režimů, kde budeme chtít změnit. Takže hacker_settings.cc_lmode je to, co se jmenuje. c_lflag. To je místo, kde jsme se dostali do bitové operátory. Jsme trochu opožděně, ale my půjdeme přes to opravdu rychle. To je místo, kde jsme se dostali do operátory bitové, kde Myslím, že jsem řekl, jeden čas dávno, že při každém spuštění zabývající se vlajkami, budete používat bitového součinu provozovateli hodně. Každý bit ve vlajce odpovídá nějaké chování. Tak tady, tento příznak má spoustu různých věcí, kde všichni z nich znamenat něco jiného. Ale to, co chci udělat, je prostě vypnout bit, který odpovídá ECHO. Takže zase, že vypnutí dělám & = ¬ ECHO. Vlastně si myslím, že je to jako TECHO, nebo tak něco. Jdu jen zkontrolovat znovu. Můžu termios to. Je to jen ECHO. ECHO se bude jeden bit. ¬ ECHO bude znamenat všechny bity jsou nastaveny na 1, což znamená, že všechny příznaky jsou nastaveny na hodnotu true s výjimkou ECHO bit. Tím končí mé místní příznaky s tím, to znamená všechny příznaky, které jsou v současné době nastaveny na hodnotu true bude stále nastavena na hodnotu true. Pokud je moje ECHO příznak nastaven na true, pak je to nutně nastavena na hodnotu false na ECHO vlajky. Takže tento řádek kódu jen vypne ECHO vlajkou. Další řádky kódu, budu jen zkopírovat v zájmu času a pak je vysvětlit. V roztoku, řekl 0. Je to asi lepší explicitně říct stdin. Všimněte si, že jsem také dělal ECHO | icanon zde. Icanon odkazuje na něco oddělený, což znamená, že kanonické režimu. Co kanonický režim znamená, je obvykle, když píšete z příkazového řádku, standard v nezpracovává nic, dokud nenarazíte nový řádek. Takže, když to GetString, zadejte spoustu věcí, pak hit nový řádek. To je, když je to poslal normy palců To je výchozí. Když jsem vypnout kanonickou režim, hned každý jednotlivý znak stisknete je to, co dostane zpracovávány, což je obvykle trochu špatně, protože je to pomalý zpracovat tyto věci, což je důvod, proč je dobré, aby se zmírnily do celých řádků. Ale chci každá postava má být zpracovány protože jsem to nechci čekat na mě zasáhnout nový řádek před zpracovává všechny znaky jsem psát. Toto vypne kanonickou režim. Tohle prostě znamená, když ve skutečnosti zpracovává znaky. To znamená, že zpracování je okamžitě, jakmile jsem psaní je, zpracovávat je. A to je funkce, která je aktualizace svoje nastavení pro standardní v, a TCSA prostředky to udělat právě teď. Další možnosti jsou počkat, až vše, co je v současné době na toku je zpracován. To opravdu nezáleží. Právě teď měnit mé nastavení je vše, co je v současné době v hacker_typer_settings. Myslím, že jsem ji nazval hacker_settings, tak se pojďme změnit. Změna všechno hacker_settings. Teď na konci našeho programu budeme chtít vrátit na to, co je v současné době uvnitř normal_settings, který se bude jen vypadat jako & normal_settings. Všimněte si, jsem se nezměnil žádný z mých normal_settings od původně dostat ji. Pak jen změnit zpět, předám je zpátky na konci. To bylo aktualizace. Dobře. Nyní uvnitř tady budu vysvětlovat kód v zájmu času. To není tak moc code. Vidíme čteme znak ze souboru. Říkali jsme, že f. Nyní si může člověk fgetc, ale jak fgetc bude fungovat je jen to bude vrátí znak, který jste právě přečetli, nebo EOF, který odpovídá konci souboru nebo nějaké chyby děje. Jsme smyčky, nadále číst znak ze souboru, dokud jsme došly znaků číst. A zatímco my děláme to, že budeme čekat na jeden znak z normy palců Pokaždé zadáte něco do příkazového řádku, že čte v charakteru od standardu a. Pak putchar se právě chystá dát char čteme tady ze souboru na standardní výstup. Můžete muž putchar, ale je to jen uvedení na standardní výstup, je to tisk, že charakter. Dalo by se také jen to, printf ("% c", c); Stejný nápad. To bude dělat většinu naší práce. Poslední věc, kterou budeme chtít udělat, je jen fclose náš soubor. Pokud nechcete fclose, že je to k nevracení paměti. Chceme fclose soubor jsme původně otevřen, a myslím, že je to ono. Učiníme-li to, že jsem už nějaké problémy. Pojďme se podívat,. Co to stěžují? Předpokládaný "int", ale argument je typu "struct _IO_FILE *". Uvidíme, jestli to funguje. Povolen pouze v C99. Augh. Dobře, aby hacker_typer. Nyní jsme se dostali další užitečné popisy. Takže použití nehlášené identifikátor "normal_settings". Nevolal jsem, že normal_settings. Nazval jsem ji current_settings. Takže pojďme změnit všechno. Teď kolem argument. Budu tuto 0 prozatím. Dobře. . / Hacker_typer cp.c. Také jsem vyčistit obrazovku na začátku. Ale můžete se podívat na poslední problém, aby viděl, jak je vymazat obrazovku. Je to prostě tisku některé znaky což se dělá to, co chci dělat. Dobře. A přemýšlet o tom, proč to musela být 0 místo standardního vstupu, , která by měla být # vymezit 0, to si stěžuje, že - Než když jsem řekl, že je to popisovače souborů, ale pak máte také svůj soubor *, popisovač souboru je jen jeden integer, vzhledem k tomu, FILE * má spoustu věcí s ním spojené. Důvod, proč jsme třeba říkat 0 místo standardního vstupu je to, že stdin je FILE *, který poukazuje na to, co je odkazování na deskriptor souboru 0. Takže i tady, když jsem si fopen (argv [1], začínám si soubor * zpět. Ale někde v tom souboru * je věc odpovídající deskriptor souboru pro daný soubor. Pokud se podíváte na manuálové stránce pro otevřené, takže myslím, že budete muset udělat člověk 3 open - Ne - man 2 open - jo. Pokud se podíváte na stránky pro otevřenou, otevřený je jako nižší úrovně fopen, a je to vrátí aktuální popisovač souboru. fopen dělá spoustu věcí na vrcholu otevřené, které namísto vrácení jen, že deskriptor souboru vrátí a celou souboru * ukazatel uvnitř které je náš malý deskriptor souboru. Takže standard se vztahuje na věci FILE *, vzhledem k tomu, 0 odkazuje jen na standardu deskriptoru souboru v sobě. Otázky? [Smích] prohnal, že. Dobrá. Skončili jsme. [Smích] [CS50.TV]