1 00:00:00,000 --> 00:00:02,490 [Powered by Google Translate] [CS50 Library] 2 00:00:02,490 --> 00:00:04,220 [Nate Hardison] [Harvard University] 3 00:00:04,220 --> 00:00:07,260 [To je CS50. CS50.TV] 4 00:00:07,260 --> 00:00:11,510 Knihovna CS50 je užitečný nástroj, který jsme nainstalovali na spotřebiči 5 00:00:11,510 --> 00:00:15,870 aby to pro vás jednodušší psát programy, které vyzve uživatele pro vstup. 6 00:00:15,870 --> 00:00:21,670 V tomto videu, budeme zatáhněte oponu a podívat se na to, co přesně je v CS50 knihovně. 7 00:00:21,670 --> 00:00:25,520 >> Ve videu na knihovny C, mluvíme o tom, jak si # include záhlaví soubory 8 00:00:25,520 --> 00:00:27,570 v knihovně ve vašem zdrojovém kódu, 9 00:00:27,570 --> 00:00:31,150 a pak propojit s binárním souboru knihovny během linkování fáze 10 00:00:31,150 --> 00:00:33,140 z procesu kompilace. 11 00:00:33,140 --> 00:00:36,440 Hlavičkové soubory specifikaci rozhraní v knihovně. 12 00:00:36,440 --> 00:00:41,280 To znamená, že detail všech zdrojů, které má knihovna k dispozici pro vás k použití, 13 00:00:41,280 --> 00:00:45,250 jako deklarace funkce, konstanty a datové typy. 14 00:00:45,250 --> 00:00:48,890 Binární soubor knihovny obsahuje implementaci knihovny, 15 00:00:48,890 --> 00:00:54,580 , který je sestaven z knihovny hlavičkové soubory a knihovny. c soubory zdrojových kódů. 16 00:00:54,580 --> 00:00:59,820 >> Binární soubor s knihovnou není moc zajímavé podívat se na to, protože, no, v binárním formátu. 17 00:00:59,820 --> 00:01:03,300 Takže, pojďme se podívat na hlavičkových souborů pro knihovnu místo. 18 00:01:03,300 --> 00:01:07,710 V tomto případě je to pouze jeden hlavičkový soubor nazvaný cs50.h. 19 00:01:07,710 --> 00:01:11,040 Jsme instalovali v uživatelské obsahovat adresář 20 00:01:11,040 --> 00:01:15,150 spolu s hlavičkovým jiném systému knihoven souborů. 21 00:01:15,150 --> 00:01:21,530 >> Jedna z prvních věcí, které si všimnete, je skutečnost, že cs50.h # obsahuje hlavičkové soubory z jiných knihoven - 22 00:01:21,530 --> 00:01:25,670 float, limity, standardní bool a standardní lib. 23 00:01:25,670 --> 00:01:28,800 Opět, podle zásady, že není objevování Ameriky, 24 00:01:28,800 --> 00:01:33,490 jsme postavili CS0 knihovny pomocí nástrojů, které ostatní stanovené pro nás. 25 00:01:33,490 --> 00:01:38,690 >> Další věc, kterou uvidíte v knihovně je, že my můžeme definovat nový typ nazvaný "string". 26 00:01:38,690 --> 00:01:42,330 Tato linka opravdu jen vytváří alias char * typ, 27 00:01:42,330 --> 00:01:46,000 takže to není magicky naplnit nový typ řetězce s atributy 28 00:01:46,000 --> 00:01:49,650 obyčejně spojené s fazolovými objekty v jiných jazycích, 29 00:01:49,650 --> 00:01:50,850 jako je délka. 30 00:01:50,850 --> 00:01:55,180 Důvod, proč jsme udělali to je ochránit nové programátory z krvavých detailů 31 00:01:55,180 --> 00:01:57,580 ukazatelů, dokud nebudou připraveni. 32 00:01:57,580 --> 00:02:00,130 >> V další části záhlaví souboru je deklarace funkcí 33 00:02:00,130 --> 00:02:04,410 že CS50 knihovna poskytuje společně s dokumentací. 34 00:02:04,410 --> 00:02:06,940 Všimněte si, že úroveň podrobností v komentářích zde. 35 00:02:06,940 --> 00:02:10,560 To je super důležité, aby lidé věděli, jak používat tyto funkce. 36 00:02:10,560 --> 00:02:19,150 Prohlašujeme, v pořadí, funguje vyzvat uživatele a návratu znaků, dvoulůžkové, plave, Ints, 37 00:02:19,150 --> 00:02:24,160 dlouho touží, a řetězce, s použitím vlastní typ řetězce. 38 00:02:24,160 --> 00:02:26,260 V souladu se zásadou skrývání informací, 39 00:02:26,260 --> 00:02:31,640 jsme dali naše definice v samostatném souboru implementace psaná v C -. cs50.c-- 40 00:02:31,640 --> 00:02:35,110 se nachází v adresáři uživatele zdroje. 41 00:02:35,110 --> 00:02:38,040 Jsme za předpokladu, že soubor, takže můžete se podívat na to, 42 00:02:38,040 --> 00:02:41,490 se z ní poučit, a překompilovat to na různých strojích, pokud si přejete, 43 00:02:41,490 --> 00:02:45,510 i když si myslíme, že je lepší pracovat na zařízení této třídy. 44 00:02:45,510 --> 00:02:47,580 Každopádně, pojďme se podívat na to teď. 45 00:02:49,020 --> 00:02:54,620 >> Funkce getchar, GetDouble, GetFloat, GetInt, a GetLongLong 46 00:02:54,620 --> 00:02:58,160 jsou postaveny na vrcholu GetString funkce. 47 00:02:58,160 --> 00:03:01,510 Ukazuje se, že všichni řídit v podstatě stejný vzor. 48 00:03:01,510 --> 00:03:04,870 Oni používají smyčky while vyzvat uživatele pro jeden řádek vstupu. 49 00:03:04,870 --> 00:03:08,430 Oni se vrátí zvláštní hodnotu, pokud uživatel vstupy prázdným řádkem. 50 00:03:08,430 --> 00:03:11,750 Snaží se analyzovat vstup uživatele jako vhodný typ, 51 00:03:11,750 --> 00:03:15,010 ať už je to char, double, float, atd. 52 00:03:15,010 --> 00:03:18,710 A pak buď vrátit, jestliže se vstup byl úspěšně analyzován 53 00:03:18,710 --> 00:03:21,330 nebo se reprompt uživatele. 54 00:03:21,330 --> 00:03:24,230 >> Na vysoké úrovni, není nic opravdu složité zde. 55 00:03:24,230 --> 00:03:28,760 Možná jste napsal podobně strukturovaná kód sami v minulosti. 56 00:03:28,760 --> 00:03:34,720 Snad nejvíce mystický vypadající část je sscanf volání, které analyzuje vstup uživatele. 57 00:03:34,720 --> 00:03:38,160 Sscanf je součástí konverze vstupního formátu rodiny. 58 00:03:38,160 --> 00:03:42,300 Žije v io.h standardní, a jeho úkolem je analyzovat řetězec C, 59 00:03:42,300 --> 00:03:46,520 podle určitého formátu, ukládání rozebrat výsledky v proměnné 60 00:03:46,520 --> 00:03:48,720 poskytuje volajícím. 61 00:03:48,720 --> 00:03:53,570 Vzhledem k tomu, vstupní funkce pro konverzi formátů jsou velmi užitečné, široce používané funkce 62 00:03:53,570 --> 00:03:56,160 že nejsou Super intuitivní nejprve, 63 00:03:56,160 --> 00:03:58,300 půjdeme nad tím, jak sscanf funguje. 64 00:03:58,300 --> 00:04:03,330 >> První argument sscanf je char * - ukazatel na znak. 65 00:04:03,330 --> 00:04:05,150 Pro funkce pracovat správně, 66 00:04:05,150 --> 00:04:08,340 že postava by měla být první znak z řetězce C, 67 00:04:08,340 --> 00:04:12,270 ukončena s nulovým \ 0 znak. 68 00:04:12,270 --> 00:04:15,120 Toto je řetězec analyzovat 69 00:04:15,120 --> 00:04:18,269 Druhým argumentem sscanf je formátovací řetězec, 70 00:04:18,269 --> 00:04:20,839 obvykle předána jako řetězcová konstanta, 71 00:04:20,839 --> 00:04:24,040 a jste mohli vidět řetězec, jako je tento před při použití printf. 72 00:04:24,040 --> 00:04:28,650 Znak procenta ve formátovacím řetězci označuje konverze specifikátor. 73 00:04:28,650 --> 00:04:30,850 Charakter bezprostředně po znak procenta, 74 00:04:30,850 --> 00:04:35,430 označuje typ C, které chceme sscanf převést na. 75 00:04:35,430 --> 00:04:40,090 V GetInt, uvidíte, že tam je% d, a% c. 76 00:04:40,090 --> 00:04:48,690 To znamená, že sscanf bude snažit desetinnou int -% d - a char -% c. 77 00:04:48,690 --> 00:04:51,510 Pro každý typ konverze v parametru řetězec formátu, 78 00:04:51,510 --> 00:04:56,620 sscanf očekává odpovídající argument, později v jeho seznamu argumentů. 79 00:04:56,620 --> 00:05:00,850 Tento argument musí směřovat k odpovídajícím zadaný místě 80 00:05:00,850 --> 00:05:04,000 do kterého se ukládají výsledek konverze. 81 00:05:04,000 --> 00:05:08,910 >> Typický způsob, jak to udělat, je vytvořit proměnnou na zásobníku před voláním sscanf 82 00:05:08,910 --> 00:05:11,440 pro každou položku, kterou chcete analyzovat z řetězce 83 00:05:11,440 --> 00:05:15,520 a pak použijte adresu operátora - ampersand - projít odkazy 84 00:05:15,520 --> 00:05:19,100 těchto proměnných do hovoru sscanf. 85 00:05:19,100 --> 00:05:22,720 Můžete vidět, že v GetInt děláme přesně to. 86 00:05:22,720 --> 00:05:28,240 Těsně před volání sscanf, jsme deklarovat int s názvem n a char volání C na zásobníku, 87 00:05:28,240 --> 00:05:32,340 a míjíme ukazatele na ně do hovoru sscanf. 88 00:05:32,340 --> 00:05:35,800 Uvedení těchto proměnných na zásobníku je přednostní přes používání prostor přidělený 89 00:05:35,800 --> 00:05:39,350 na haldě s malloc, protože se vyhnete režii malloc volání, 90 00:05:39,350 --> 00:05:43,060 a nemusíte se starat o úniku paměti. 91 00:05:43,060 --> 00:05:47,280 Znaky nejsou předponou znakem procent nepobízejí konverzi. 92 00:05:47,280 --> 00:05:50,380 Spíše se jen přidat k specifikaci formátu. 93 00:05:50,380 --> 00:05:56,500 >> Například, v případě, že řetězec formátu v GetInt byly% d místo, 94 00:05:56,500 --> 00:05:59,800 sscanf vypadat za písmenem a následuje int, 95 00:05:59,800 --> 00:06:04,360 a když se pokusí převést int, to by nebylo nic jiného s A. 96 00:06:04,360 --> 00:06:07,440 Jedinou výjimkou je mezera. 97 00:06:07,440 --> 00:06:11,030 Bílé mezery ve formátovacím řetězci nalezeny žádné množství mezerou - 98 00:06:11,030 --> 00:06:12,890 dokonce žádný vůbec. 99 00:06:12,890 --> 00:06:18,100 Takže, to je důvod, proč komentář zmiňuje případně s předními a / nebo koncové mezery. 100 00:06:18,100 --> 00:06:22,910 Takže, bude v tomto bodě to vypadá jako naše sscanf hovoru pokusí analyzovat uživatele vstupní řetězec 101 00:06:22,910 --> 00:06:25,380 a kontrolovat případné vedoucí mezerou, 102 00:06:25,380 --> 00:06:29,300 následuje int, která bude konvertována a uloženy v int proměnné n 103 00:06:29,300 --> 00:06:33,090 následuje nějakého množství mezery, a následuje znak 104 00:06:33,090 --> 00:06:35,810 uložen v proměnné char c. 105 00:06:35,810 --> 00:06:37,790 >> Co návratové hodnoty? 106 00:06:37,790 --> 00:06:41,560 Sscanf bude analyzovat vstupní řádku od začátku do konce, 107 00:06:41,560 --> 00:06:44,860 zastavení, když dosáhne konce nebo když charakter ve vstupním 108 00:06:44,860 --> 00:06:49,320 neodpovídá formátu charakter nebo pokud není možné uskutečnit převod. 109 00:06:49,320 --> 00:06:52,690 Je to návrat hodnota se používá vyčlenit, když se zastavil. 110 00:06:52,690 --> 00:06:55,670 Pokud se zastavil, protože na konci vstupního řetězce 111 00:06:55,670 --> 00:07:00,630 před provedením jakékoli konverze a před tím, že zápas část formátovací řetězec, 112 00:07:00,630 --> 00:07:04,840 pak speciální konstanta EOF je vrácena. 113 00:07:04,840 --> 00:07:08,200 V opačném případě vrátí počet úspěšných konverzí, 114 00:07:08,200 --> 00:07:14,380 , které by mohly být 0, 1 nebo 2, protože jsme požádáni o dvě konverze. 115 00:07:14,380 --> 00:07:19,000 V našem případě chceme, aby se ujistil, že uživatel napsáno v int a pouze na int. 116 00:07:19,000 --> 00:07:23,370 >> Takže, chceme sscanf vrátit 1. Podívejte se, proč? 117 00:07:23,370 --> 00:07:26,850 Pokud sscanf vrátil 0, pak žádné převody byly provedeny, 118 00:07:26,850 --> 00:07:31,690 takže uživatel napsal něco jiného než int na začátku vstupu. 119 00:07:31,690 --> 00:07:37,100 Pokud sscanf vrátí 2, pak uživatel se řádně napište ji na začátku vstupu, 120 00:07:37,100 --> 00:07:41,390 ale oni pak psali v nějakém non-prázdný znak poté 121 00:07:41,390 --> 00:07:44,940 od% c převod úspěšný. 122 00:07:44,940 --> 00:07:49,570 Wow, to je docela zdlouhavý vysvětlení jednom volání funkce. 123 00:07:49,570 --> 00:07:53,460 Každopádně, pokud chcete více informací o sscanf a jeho sourozenci, 124 00:07:53,460 --> 00:07:57,130 podívejte se na manuálové stránky, Google, nebo obojí. 125 00:07:57,130 --> 00:07:58,780 Existuje spousta možností formátovacích řetězců, 126 00:07:58,780 --> 00:08:03,830 a ty vám může ušetřit spoustu manuální práce, když se snaží analyzovat řetězce v jazyce C. 127 00:08:03,830 --> 00:08:07,180 >> Konečná funkce v knihovně je podívat se na GetString. 128 00:08:07,180 --> 00:08:10,310 Ukazuje se, že GetString je složité funkce psát správně, 129 00:08:10,310 --> 00:08:14,290 i když se to zdá jako takový jednoduchý, společnému úkolu. 130 00:08:14,290 --> 00:08:16,170 Proč je tomu tak? 131 00:08:16,170 --> 00:08:21,380 No, pojďme si o tom, jak budeme ukládat řádek, že uživatel zadá v. 132 00:08:21,380 --> 00:08:23,880 Vzhledem k tomu, řetězec je posloupnost znaků, 133 00:08:23,880 --> 00:08:26,430 bychom mohli chtít uložit jej do pole na zásobníku, 134 00:08:26,430 --> 00:08:31,250 ale potřebovali bychom vědět, jak dlouho pole bude, když jsme prohlásit ho. 135 00:08:31,250 --> 00:08:34,030 Stejně tak, pokud chceme, aby to na hromadu, 136 00:08:34,030 --> 00:08:38,090 musíme projít malloc počet bajtů chceme rezervy, 137 00:08:38,090 --> 00:08:39,730 ale to je nemožné. 138 00:08:39,730 --> 00:08:42,760 Nemáme ponětí, kolik znaků bude uživatel psát, 139 00:08:42,760 --> 00:08:46,590 předtím, než uživatel skutečně nemá psát. 140 00:08:46,590 --> 00:08:50,720 >> Naivní řešení tohoto problému je jen rezervovat velký kus prostoru, řekněme, 141 00:08:50,720 --> 00:08:54,540 blok 1000 znaků pro vstup uživatele, 142 00:08:54,540 --> 00:08:57,980 za předpokladu, že uživatel by nikdy zadat řetězec, který dlouho. 143 00:08:57,980 --> 00:09:00,810 To je špatný nápad to ze dvou důvodů. 144 00:09:00,810 --> 00:09:05,280 Za prvé, za předpokladu, že uživatelé obvykle nemají psát v řetězcích tak dlouho, 145 00:09:05,280 --> 00:09:07,610 můžete odpad spoustu paměti. 146 00:09:07,610 --> 00:09:10,530 Na moderních strojích, mohlo by to být problém, pokud to 147 00:09:10,530 --> 00:09:13,890 v jednom nebo dvou ojedinělých případech, 148 00:09:13,890 --> 00:09:17,630 ale pokud užíváte vstup uživatele do smyčky a skladování pro pozdější použití, 149 00:09:17,630 --> 00:09:20,870 můžete rychle vysát spoustu paměti. 150 00:09:20,870 --> 00:09:24,450 Navíc, pokud se program píšete je pro menší počítače - 151 00:09:24,450 --> 00:09:28,100 zařízení, jako je smartphone, nebo něco jiného s omezenou pamětí - 152 00:09:28,100 --> 00:09:32,060 toto řešení bude způsobovat problémy mnohem rychleji. 153 00:09:32,060 --> 00:09:36,450 Druhý, vážnější důvod to udělat, je, že opustí svůj program zranitelné 154 00:09:36,450 --> 00:09:39,710 na to, co se nazývá přetečení vyrovnávací paměti útok. 155 00:09:39,710 --> 00:09:45,840 V programování, vyrovnávací paměť je paměť slouží k dočasnému ukládání vstupních nebo výstupních dat, 156 00:09:45,840 --> 00:09:48,980 což v tomto případě je naše 1000-char blok. 157 00:09:48,980 --> 00:09:53,370 Přetečení dochází při zápisu dat za koncem bloku. 158 00:09:53,370 --> 00:09:57,790 >> Například, jestliže uživatel skutečně dělá typ ve více než 1000 znaků. 159 00:09:57,790 --> 00:10:01,570 Možná jste to zažil náhodně při programování s poli. 160 00:10:01,570 --> 00:10:05,620 Pokud máte řadu 10 ints, nic nezastaví vás z pokusu o čtení nebo zápis 161 00:10:05,620 --> 00:10:07,810 15. int. 162 00:10:07,810 --> 00:10:10,000 Nejsou žádné upozornění kompilátoru nebo chyby. 163 00:10:10,000 --> 00:10:13,250 Program jen přehmatů rovně a přistupuje paměti 164 00:10:13,250 --> 00:10:18,150 kde si myslí, že 15. int bude, a to může přepsat své další proměnné. 165 00:10:18,150 --> 00:10:22,040 V nejhorším případě můžete přepsat některé z vašich programu vnitřní 166 00:10:22,040 --> 00:10:26,820 kontrolní mechanismy, což váš program, aby skutečně provést různé instrukce 167 00:10:26,820 --> 00:10:28,340 , než jste zamýšleli. 168 00:10:28,340 --> 00:10:31,360 >> Nyní, to není běžné, aby to náhodou, 169 00:10:31,360 --> 00:10:35,150 ale je to docela obyčejná technika, že protivníci používají rozbít programy 170 00:10:35,150 --> 00:10:39,080 a dal škodlivého kódu na počítači jiných lidí. 171 00:10:39,080 --> 00:10:42,910 Proto lze nejen použít náš naivní řešení. 172 00:10:42,910 --> 00:10:45,590 Potřebujeme způsob, jak zabránit naše programy od bytí zranitelné 173 00:10:45,590 --> 00:10:47,880 k přetečení vyrovnávací paměti útoku. 174 00:10:47,880 --> 00:10:51,430 Chcete-li to, musíme se ujistit, že naše vyrovnávací paměti může růst, jak jsme si 175 00:10:51,430 --> 00:10:53,850 více vstup od uživatele. 176 00:10:53,850 --> 00:10:57,440 Řešení? Používáme haldy přidělené vyrovnávací paměti. 177 00:10:57,440 --> 00:10:59,950 Vzhledem k tomu, můžeme změnit jeho velikost pomocí resize na realloc funkci, 178 00:10:59,950 --> 00:11:04,580 a sledujeme ze dvou čísel - index dalšího prázdného slotu ve vyrovnávací paměti 179 00:11:04,580 --> 00:11:08,390 a délka nebo kapacita pufru. 180 00:11:08,390 --> 00:11:13,210 Čteme v znaků z uživatelského jeden po druhém pomocí fgetc funkci. 181 00:11:13,210 --> 00:11:19,360 Argument fgetc funkce má - stdin - je odkaz na standardní vstupní řetězec, 182 00:11:19,360 --> 00:11:23,810 který je preconnected vstupní kanál, který se používá pro přenos vstup uživatele 183 00:11:23,810 --> 00:11:26,270 z terminálu do programu. 184 00:11:26,270 --> 00:11:29,890 >> Kdykoli uživatel zadá do nového charakteru, můžeme zkontrolovat, zda index 185 00:11:29,890 --> 00:11:35,810 dalšího volného slotu plus 1 je větší, než je kapacita vyrovnávací paměti. 186 00:11:35,810 --> 00:11:39,690 1 je v, protože pokud příští zdarma index je 5, 187 00:11:39,690 --> 00:11:44,150 pak naše vyrovnávací paměti délka musí být 6 díky 0 indexování. 188 00:11:44,150 --> 00:11:48,350 Pokud jsme došly prostoru ve vyrovnávací paměti, pak se snažíme změnit jeho velikost, 189 00:11:48,350 --> 00:11:51,690 zdvojnásobení to tak, že se snížit na to, kolikrát jsme velikost 190 00:11:51,690 --> 00:11:54,760 v případě, že uživatel je psaní v opravdu dlouhé řetězce. 191 00:11:54,760 --> 00:11:57,950 Pokud řetězec dostal příliš dlouho, nebo když narazíme z haldy paměti, 192 00:11:57,950 --> 00:12:01,350 jsme osvobodit naše vyrovnávací paměti a vrátí hodnotu NULL. 193 00:12:01,350 --> 00:12:04,170 >> Nakonec, přidáme char do vyrovnávací paměti. 194 00:12:04,170 --> 00:12:08,200 Jakmile uživatel zmáčkne Enter nebo Return, což signalizuje nový řádek, 195 00:12:08,200 --> 00:12:12,050 nebo speciální znak - kontrolní d - což signalizuje konec vstupu, 196 00:12:12,050 --> 00:12:16,240 děláme Zkontrolujte, zda uživatel skutečně zadali vůbec nic. 197 00:12:16,240 --> 00:12:18,820 Pokud ne, vrátíme null. 198 00:12:18,820 --> 00:12:22,280 V opačném případě, protože náš vyrovnávací paměti je pravděpodobně větší, než potřebujeme, 199 00:12:22,280 --> 00:12:24,830 v nejhorším případě je to téměř dvakrát tak velká jako je třeba 200 00:12:24,830 --> 00:12:27,830 protože jsme dvakrát pokaždé, když jsme změnit velikost, 201 00:12:27,830 --> 00:12:31,840 uděláme novou kopii řetězce, pouze pomocí množství prostoru, který potřebujeme. 202 00:12:31,840 --> 00:12:34,220 Jsme přidat další 1 až malloc volání, 203 00:12:34,220 --> 00:12:37,810 tak, že je prostor pro speciální nulový znak terminátoru - \ 0, 204 00:12:37,810 --> 00:12:41,990 které přidáme do řetězce, jakmile budeme kopírovat ve zbytku postav, 205 00:12:41,990 --> 00:12:45,060 pomocí strncpy místo strcpy 206 00:12:45,060 --> 00:12:48,830 takže můžeme přesně určit, kolik znaků chceme kopírovat. 207 00:12:48,830 --> 00:12:51,690 Strcpy kopíruje, dokud nenarazí na \ 0. 208 00:12:51,690 --> 00:12:55,740 Pak jsme osvobodit naše vyrovnávací paměti a vrátí kopii volajícímu. 209 00:12:55,740 --> 00:12:59,840 >> Kdo věděl, že takový jednoduchý vyhlížející funkce by měla být tak složité? 210 00:12:59,840 --> 00:13:02,820 Teď už víte, co jde do CS50 knihovny. 211 00:13:02,820 --> 00:13:06,470 >> Mé jméno je Nate Hardison, a to je CS50. 212 00:13:06,470 --> 00:13:08,350 [CS50.TV]