1 00:00:00,000 --> 00:00:02,490 [Powered by Google Translate] [CS50 bibliotek] 2 00:00:02,490 --> 00:00:04,220 [Nate Hardison] [Harvard University] 3 00:00:04,220 --> 00:00:07,260 [Detta är CS50. CS50.TV] 4 00:00:07,260 --> 00:00:11,510 Den CS50 Biblioteket är ett användbart verktyg som vi har installerat på apparaten 5 00:00:11,510 --> 00:00:15,870 att göra det lättare för dig att skriva program som snabba användare för inmatning. 6 00:00:15,870 --> 00:00:21,670 I den här videon kommer vi dra tillbaka gardinen och titta på vad exakt är i CS50 biblioteket. 7 00:00:21,670 --> 00:00:25,520 >> I videon på C-bibliotek, talar vi om hur du gör # include rubriker filer 8 00:00:25,520 --> 00:00:27,570 av biblioteket i din källkod, 9 00:00:27,570 --> 00:00:31,150 och då du länkar med en binär biblioteksfil under länka fasen 10 00:00:31,150 --> 00:00:33,140 av beräkningen processen. 11 00:00:33,140 --> 00:00:36,440 De header-filer anger gränssnittet biblioteket. 12 00:00:36,440 --> 00:00:41,280 Det vill säga de ingående alla de resurser som biblioteket har tillgängliga för dig att använda, 13 00:00:41,280 --> 00:00:45,250 Liksom funktionsdeklarationer, konstanter och datatyper. 14 00:00:45,250 --> 00:00:48,890 Den binära biblioteksfil innehåller genomförandet av biblioteket, 15 00:00:48,890 --> 00:00:54,580 som sammanställs av bibliotekets header-filer och Bibliotekets. c källkodsfiler. 16 00:00:54,580 --> 00:00:59,820 >> Den binära biblioteksfil är inte särskilt intressant att titta på eftersom det är, ja, i binär. 17 00:00:59,820 --> 00:01:03,300 Så, låt oss ta en titt på header-filer för biblioteket istället. 18 00:01:03,300 --> 00:01:07,710 I det här fallet, det finns bara en header fil som heter cs50.h. 19 00:01:07,710 --> 00:01:11,040 Vi har installerat det i användarens inkluderar katalogen 20 00:01:11,040 --> 00:01:15,150 tillsammans med de andra systemet bibliotekens header-filer. 21 00:01:15,150 --> 00:01:21,530 >> En av de första sakerna du kommer att märka är att cs50.h # omfattar header-filer från andra bibliotek - 22 00:01:21,530 --> 00:01:25,670 float, gränser, standard bool och standard lib. 23 00:01:25,670 --> 00:01:28,800 Återigen, efter principen att inte uppfinna hjulet, 24 00:01:28,800 --> 00:01:33,490 Vi har byggt CS0 biblioteket med hjälp av verktyg som andra för oss. 25 00:01:33,490 --> 00:01:38,690 >> Nästa sak du ser i biblioteket är att vi definierar en ny typ som kallas "sträng". 26 00:01:38,690 --> 00:01:42,330 Denna linje egentligen bara skapar ett alias för char * typ, 27 00:01:42,330 --> 00:01:46,000 så det inte genomsyra magiskt den nya strängen typ med attribut 28 00:01:46,000 --> 00:01:49,650 vanligen förknippas med sträng objekt i andra språk, 29 00:01:49,650 --> 00:01:50,850 såsom längd. 30 00:01:50,850 --> 00:01:55,180 Anledningen till att vi har gjort är att skydda nya programmerare från de blodiga detaljerna 31 00:01:55,180 --> 00:01:57,580 pekare tills de är redo. 32 00:01:57,580 --> 00:02:00,130 >> Nästa del av huvudet filen är deklarationen av de funktioner 33 00:02:00,130 --> 00:02:04,410 att CS50 biblioteket tillhandahåller tillsammans med dokumentation. 34 00:02:04,410 --> 00:02:06,940 Lägg märke till detaljnivå i kommentarerna här. 35 00:02:06,940 --> 00:02:10,560 Detta är super viktigt så att folk vet hur man använder dessa funktioner. 36 00:02:10,560 --> 00:02:19,150 Vi förklarar i sin tur fungerar att uppmana användaren och tecken avkastning, dubbel, flottar, Ints, 37 00:02:19,150 --> 00:02:24,160 lång längtar, och stråkar, med hjälp av vår egen sträng typ. 38 00:02:24,160 --> 00:02:26,260 Enlighet med principen om information hiding, 39 00:02:26,260 --> 00:02:31,640 vi har lagt vår definition i en separat c implementering fil -. cs50.c-- 40 00:02:31,640 --> 00:02:35,110 finns i användarens källkatalogen. 41 00:02:35,110 --> 00:02:38,040 Vi har förutsatt att filen så att du kan ta en titt på det, 42 00:02:38,040 --> 00:02:41,490 lära av det, och kompilera den på olika maskiner om du vill, 43 00:02:41,490 --> 00:02:45,510 även om vi tycker att det är bättre att arbeta på apparaten för denna klass. 44 00:02:45,510 --> 00:02:47,580 Hur som helst, låt oss ta en titt på det nu. 45 00:02:49,020 --> 00:02:54,620 >> Funktionerna getchar, GetDouble, getFloat, getInt, och GetLongLong 46 00:02:54,620 --> 00:02:58,160 är alla byggda ovanpå GetString funktionen. 47 00:02:58,160 --> 00:03:01,510 Det visar sig att de alla följer i huvudsak samma mönster. 48 00:03:01,510 --> 00:03:04,870 De använder en while-slinga för att uppmana användaren att en rad av ingång. 49 00:03:04,870 --> 00:03:08,430 De återvänder ett speciellt värde om användaren matar en tom rad. 50 00:03:08,430 --> 00:03:11,750 De försöker att tolka användarens input som lämplig typ, 51 00:03:11,750 --> 00:03:15,010 det är en röding, en dubbel, en flottör etc. 52 00:03:15,010 --> 00:03:18,710 Och då de återvänder antingen resultatet om ingången framgångsrikt tolkas 53 00:03:18,710 --> 00:03:21,330 eller de Fråga igen användaren. 54 00:03:21,330 --> 00:03:24,230 >> På en hög nivå, det finns inget riktigt knepigt här. 55 00:03:24,230 --> 00:03:28,760 Du kanske har skrivit liknande strukturerad kod själv i det förflutna. 56 00:03:28,760 --> 00:03:34,720 Den kanske mest kryptiska utseende del är sscanf samtal som tolkar användarens input. 57 00:03:34,720 --> 00:03:38,160 Sscanf är en del av den ingående formatkonvertering familj. 58 00:03:38,160 --> 00:03:42,300 Den lever i standard io.h och dess uppgift är att tolka en C sträng, 59 00:03:42,300 --> 00:03:46,520 enligt ett visst format, lagrar parse resulterar i variabel 60 00:03:46,520 --> 00:03:48,720 tillhandahålls av uppringaren. 61 00:03:48,720 --> 00:03:53,570 Eftersom de ingående formatkonvertering funktioner är mycket användbara, ofta använda funktioner 62 00:03:53,570 --> 00:03:56,160 som är inte super intuitiv först, 63 00:03:56,160 --> 00:03:58,300 Vi ska gå över hur sscanf fungerar. 64 00:03:58,300 --> 00:04:03,330 >> Det första argumentet till sscanf är en char * - en pekare till ett tecken. 65 00:04:03,330 --> 00:04:05,150 För att funktionen ska fungera korrekt, 66 00:04:05,150 --> 00:04:08,340 det tecknet bör vara det första tecknet i en C sträng, 67 00:04:08,340 --> 00:04:12,270 avslutas med null \ 0 tecken. 68 00:04:12,270 --> 00:04:15,120 Detta är den sträng som ska tolka 69 00:04:15,120 --> 00:04:18,269 Det andra argumentet för sscanf är ett format sträng, 70 00:04:18,269 --> 00:04:20,839 vanligtvis skickas in som en sträng konstant, 71 00:04:20,839 --> 00:04:24,040 och du kanske har sett en sträng så här innan när du använder printf. 72 00:04:24,040 --> 00:04:28,650 En procent tecken i formatsträngen indikerar en formatbeskrivning. 73 00:04:28,650 --> 00:04:30,850 Tecknet omedelbart efter ett procenttecken, 74 00:04:30,850 --> 00:04:35,430 anger C-typ som vi vill sscanf konvertera till. 75 00:04:35,430 --> 00:04:40,090 I getInt ser du att det finns en% d och en% c. 76 00:04:40,090 --> 00:04:48,690 Detta innebär att sscanf försöker en decimal int -% d - och röding - det% c. 77 00:04:48,690 --> 00:04:51,510 För varje formatbeskrivning i formatsträngen, 78 00:04:51,510 --> 00:04:56,620 sscanf förväntar sig en motsvarande resonemang senare i sin argumentation listan. 79 00:04:56,620 --> 00:05:00,850 Detta argument måste peka på en lämpligt skrivit plats 80 00:05:00,850 --> 00:05:04,000 där du vill spara resultatet av konverteringen. 81 00:05:04,000 --> 00:05:08,910 >> Det typiska sättet att göra detta är att skapa en variabel på stacken innan sscanf samtalet 82 00:05:08,910 --> 00:05:11,440 för varje objekt som du vill tolka från strängen 83 00:05:11,440 --> 00:05:15,520 och sedan använda adressen operatören - et-tecknet - att passera pekare 84 00:05:15,520 --> 00:05:19,100 dessa variabler till sscanf anropet. 85 00:05:19,100 --> 00:05:22,720 Du kan se det i getInt vi göra just detta. 86 00:05:22,720 --> 00:05:28,240 Strax före sscanf samtalet deklarerar vi en int som kallas n och en röding samtal c på stacken, 87 00:05:28,240 --> 00:05:32,340 och vi passerar pekare till dem i sscanf samtalet. 88 00:05:32,340 --> 00:05:35,800 Att sätta dessa variabler på stacken är att föredra framför att använda utrymme allokerat 89 00:05:35,800 --> 00:05:39,350 på högen med malloc, eftersom du undviker overhead av malloc samtalet 90 00:05:39,350 --> 00:05:43,060 och du behöver inte oroa dig för läckande minne. 91 00:05:43,060 --> 00:05:47,280 Tecken som inte föregås av ett procenttecken inte ber inte konvertering. 92 00:05:47,280 --> 00:05:50,380 Snarare de lägger bara till formatet specifikationen. 93 00:05:50,380 --> 00:05:56,500 >> Till exempel, om formatsträngen i getInt var en% d istället, 94 00:05:56,500 --> 00:05:59,800 sscanf skulle leta efter bokstaven A följt av en int, 95 00:05:59,800 --> 00:06:04,360 och även om det skulle försöka omvandla int skulle det inte göra något annat med en. 96 00:06:04,360 --> 00:06:07,440 Det enda undantaget är mellanslag. 97 00:06:07,440 --> 00:06:11,030 Tomrum i formatsträngen matcha alla belopp blanktecken - 98 00:06:11,030 --> 00:06:12,890 inget alls. 99 00:06:12,890 --> 00:06:18,100 Så, det är därför kommentaren nämner möjligen med ledande och / eller avslutande blanksteg. 100 00:06:18,100 --> 00:06:22,910 Så kommer vid denna tidpunkt ser det ut som vår sscanf samtal försöker tolka användarens input strängen 101 00:06:22,910 --> 00:06:25,380 genom att kontrollera eventuell ledande mellanslag, 102 00:06:25,380 --> 00:06:29,300 följt av en int som ska konverteras och lagras i int variabeln n 103 00:06:29,300 --> 00:06:33,090 följt av en viss mängd av blanktecken, och följs av ett tecken 104 00:06:33,090 --> 00:06:35,810 lagras i CHAR-variabel c.. 105 00:06:35,810 --> 00:06:37,790 >> Hur är det returnerade värdet? 106 00:06:37,790 --> 00:06:41,560 Sscanf kommer tolka inmatningsraden från början till slut, 107 00:06:41,560 --> 00:06:44,860 stanna när den når slutet eller när ett tecken i indata 108 00:06:44,860 --> 00:06:49,320 matchar inte ett format karaktär eller när det inte kan göra en konvertering. 109 00:06:49,320 --> 00:06:52,690 Det är returvärdet används för att skilja när det slutat. 110 00:06:52,690 --> 00:06:55,670 Om det slutade, eftersom det nått slutet av den ingående strängen 111 00:06:55,670 --> 00:07:00,630 innan du gör några omvandlingar och innan att inte matcha en del av formatsträngen, 112 00:07:00,630 --> 00:07:04,840 då den speciella konstanta EOF returneras. 113 00:07:04,840 --> 00:07:08,200 Annars returneras antalet lyckade omvandlingar, 114 00:07:08,200 --> 00:07:14,380 som kan vara 0, 1 eller 2, eftersom vi har bett om två omvandlingar. 115 00:07:14,380 --> 00:07:19,000 I vårt fall vill vi se till att användaren har skrivit in en int och bara en int. 116 00:07:19,000 --> 00:07:23,370 >> Så vill vi sscanf återvända 1. Se varför? 117 00:07:23,370 --> 00:07:26,850 Om sscanf gav 0, då inga omvandlingar gjordes, 118 00:07:26,850 --> 00:07:31,690 så att användaren skrivit något annat än en int i början av inmatningen. 119 00:07:31,690 --> 00:07:37,100 Om sscanf returnerar 2, då användaren har korrekt skriver du den i början av inmatningen, 120 00:07:37,100 --> 00:07:41,390 men de skrev då i vissa icke-blanktecken karaktär efteråt 121 00:07:41,390 --> 00:07:44,940 eftersom% c omvandlingen lyckades. 122 00:07:44,940 --> 00:07:49,570 Wow, det är ganska lång förklaring till en funktionsanrop. 123 00:07:49,570 --> 00:07:53,460 Hur som helst, om du vill ha mer information om sscanf och dess syskon, 124 00:07:53,460 --> 00:07:57,130 kolla in man-sidor, Google, eller både och. 125 00:07:57,130 --> 00:07:58,780 Det finns massor av alternativ formatsträngen 126 00:07:58,780 --> 00:08:03,830 och dessa kan spara dig mycket manuellt arbete när man försöker tolka strängar i C. 127 00:08:03,830 --> 00:08:07,180 >> Den sista funktionen i biblioteket för att titta på är GetString. 128 00:08:07,180 --> 00:08:10,310 Det visar sig att GetString är en knepig funktion för att skriva korrekt, 129 00:08:10,310 --> 00:08:14,290 även om det verkar vara en sådan enkel, vanlig uppgift. 130 00:08:14,290 --> 00:08:16,170 Varför är det så? 131 00:08:16,170 --> 00:08:21,380 Nåväl, låt oss tänka på hur vi ska lagra den linje som användaren skriver i. 132 00:08:21,380 --> 00:08:23,880 Eftersom en sträng är en sekvens av tecken, 133 00:08:23,880 --> 00:08:26,430 Vi kanske vill lagra den i en array på stacken, 134 00:08:26,430 --> 00:08:31,250 men vi skulle behöva veta hur länge arrayen kommer att bli när vi förklara den. 135 00:08:31,250 --> 00:08:34,030 Likaså om vi vill sätta den på högen, 136 00:08:34,030 --> 00:08:38,090 Vi behöver skicka till malloc antalet byte vill vi reservera, 137 00:08:38,090 --> 00:08:39,730 men detta är omöjligt. 138 00:08:39,730 --> 00:08:42,760 Vi har ingen aning om hur många tecken användaren skriver in 139 00:08:42,760 --> 00:08:46,590 innan användaren faktiskt inte skriva dem. 140 00:08:46,590 --> 00:08:50,720 >> En naiv lösning på detta problem är att bara reservera en stor bit av rymden, säger 141 00:08:50,720 --> 00:08:54,540 ett block med 1000 tecken för användarens inmatning, 142 00:08:54,540 --> 00:08:57,980 förutsatt att användaren aldrig skulle skriva in en sträng så länge. 143 00:08:57,980 --> 00:09:00,810 Detta är en dålig idé av två skäl. 144 00:09:00,810 --> 00:09:05,280 Först, förutsatt att användarna oftast inte skriver i strängar så länge, 145 00:09:05,280 --> 00:09:07,610 kan du slösa bort en massa minne. 146 00:09:07,610 --> 00:09:10,530 På moderna maskiner, kan detta inte vara ett problem om du gör detta 147 00:09:10,530 --> 00:09:13,890 i en eller två enstaka fall, 148 00:09:13,890 --> 00:09:17,630 men om du tar användarens inmatning i en slinga och lagring för senare användning, 149 00:09:17,630 --> 00:09:20,870 kan du snabbt suga upp massor av minne. 150 00:09:20,870 --> 00:09:24,450 Dessutom, om det program du skriver är för en mindre dator - 151 00:09:24,450 --> 00:09:28,100 en enhet som en smartphone eller något annat med begränsat minne - 152 00:09:28,100 --> 00:09:32,060 denna lösning kommer att orsaka problem mycket snabbare. 153 00:09:32,060 --> 00:09:36,450 Den andra, allvarligare anledning att inte göra detta är att den lämnar ditt program sårbar 154 00:09:36,450 --> 00:09:39,710 till vad som kallas en buffer overflow attack. 155 00:09:39,710 --> 00:09:45,840 I programmering är ett buffertminne som används för att tillfälligt lagra in-eller utdata, 156 00:09:45,840 --> 00:09:48,980 som i detta fall är vår 1000-char blocket. 157 00:09:48,980 --> 00:09:53,370 Ett buffertspill inträffar när data skrivs förbi slutet av blocket. 158 00:09:53,370 --> 00:09:57,790 >> Till exempel, om en användare faktiskt gör typ i mer än 1000 tecken. 159 00:09:57,790 --> 00:10:01,570 Du kanske har upplevt detta misstag vid programmering med arrayer. 160 00:10:01,570 --> 00:10:05,620 Om du har en mängd 10 Ints, stoppar inget du från att försöka läsa eller skriva 161 00:10:05,620 --> 00:10:07,810 den 15: e Int. 162 00:10:07,810 --> 00:10:10,000 Det finns inga kompileringsvarningar eller fel. 163 00:10:10,000 --> 00:10:13,250 Programmet misstag bara rakt fram och åtkomst till minnet 164 00:10:13,250 --> 00:10:18,150 där den tror den 15: e int kommer att vara, och detta kan skriva över dina andra variabler. 165 00:10:18,150 --> 00:10:22,040 I värsta fall kan du skriva en del av din programmets interna 166 00:10:22,040 --> 00:10:26,820 kontrollmekanismer, för att orsaka ditt program faktiskt exekvera olika instruktioner 167 00:10:26,820 --> 00:10:28,340 än du tänkt. 168 00:10:28,340 --> 00:10:31,360 >> Nu är det inte vanligt att göra detta misstag, 169 00:10:31,360 --> 00:10:35,150 men detta är en ganska vanlig teknik att skurkarna använder för att bryta program 170 00:10:35,150 --> 00:10:39,080 och sätta skadlig kod på andras datorer. 171 00:10:39,080 --> 00:10:42,910 Därför kan vi inte bara använda vår naiva lösning. 172 00:10:42,910 --> 00:10:45,590 Vi behöver ett sätt att förhindra våra program från att sårbara 173 00:10:45,590 --> 00:10:47,880 för ett buffertspill attack. 174 00:10:47,880 --> 00:10:51,430 För att göra detta måste vi se till att våra buffert kan växa när vi läser 175 00:10:51,430 --> 00:10:53,850 mer input från användaren. 176 00:10:53,850 --> 00:10:57,440 Lösningen? Vi använder en buffert hög allokerad. 177 00:10:57,440 --> 00:10:59,950 Eftersom vi kan skala med Ändra storlek på realloc funktionen 178 00:10:59,950 --> 00:11:04,580 och vi håller koll på två nummer - index för nästa tomma plats i bufferten 179 00:11:04,580 --> 00:11:08,390 och längden eller buffertens. 180 00:11:08,390 --> 00:11:13,210 Vi läser i tecken från användaren en i taget med hjälp av fgetc funktionen. 181 00:11:13,210 --> 00:11:19,360 Argumentet att fgetc funktionen tar - stdin - är en referens till den standard input strängen, 182 00:11:19,360 --> 00:11:23,810 vilket är en preconnected inkanal som används för att överföra användarens inmatning 183 00:11:23,810 --> 00:11:26,270 från terminalen till programmet. 184 00:11:26,270 --> 00:11:29,890 >> När användaren skriver i en ny karaktär, kontrollerar vi att se om indexet 185 00:11:29,890 --> 00:11:35,810 av nästa fria plats plus 1 är större än kapaciteten hos bufferten. 186 00:11:35,810 --> 00:11:39,690 Den 1 kommer in för om nästa lediga indexet 5, 187 00:11:39,690 --> 00:11:44,150 då vår buffertens längd måste vara 6 tack vare 0 indexering. 188 00:11:44,150 --> 00:11:48,350 Om vi ​​har slut på utrymme i bufferten, då vi försöker ändra storlek på den, 189 00:11:48,350 --> 00:11:51,690 fördubbling det så att vi skär ner på antalet gånger som vi ändra storlek 190 00:11:51,690 --> 00:11:54,760 om användaren skriver i en riktigt lång sträng. 191 00:11:54,760 --> 00:11:57,950 Om strängen har blivit för lång eller om vi får slut på hög minne, 192 00:11:57,950 --> 00:12:01,350 Vi frigör vår buffert och returnera null. 193 00:12:01,350 --> 00:12:04,170 >> Slutligen, lägga vi kolet till bufferten. 194 00:12:04,170 --> 00:12:08,200 När användaren träffar in eller tillbaka, signalerar en ny rad, 195 00:12:08,200 --> 00:12:12,050 eller den speciella tecken - kontroll d - som signalerar ett slut på ingång, 196 00:12:12,050 --> 00:12:16,240 Vi gör en kontroll för att se om användaren faktiskt skrivit i någonting alls. 197 00:12:16,240 --> 00:12:18,820 Om inte, återvänder vi noll. 198 00:12:18,820 --> 00:12:22,280 Annars eftersom vår buffert är förmodligen större än vi behöver, 199 00:12:22,280 --> 00:12:24,830 i värsta fall är det nästan dubbelt så stor som vi behöver 200 00:12:24,830 --> 00:12:27,830 eftersom vi fördubbla varje gång vi ändrar storlek, 201 00:12:27,830 --> 00:12:31,840 Vi gör en ny kopia av strängen med bara mängden utrymme som vi behöver. 202 00:12:31,840 --> 00:12:34,220 Vi lägger till en extra 1 till malloc samtalet 203 00:12:34,220 --> 00:12:37,810 så att det finns utrymme för den speciella null terminator karaktär - \ 0, 204 00:12:37,810 --> 00:12:41,990 som vi lägga till strängen när vi kopierar i resten av karaktärerna, 205 00:12:41,990 --> 00:12:45,060 använda strncpy istället för strcpy 206 00:12:45,060 --> 00:12:48,830 så att vi kan ange exakt hur många tecken som vi vill kopiera. 207 00:12:48,830 --> 00:12:51,690 Strcpy kopior tills den träffar en \ 0. 208 00:12:51,690 --> 00:12:55,740 Då vi frigöra vår buffert och återsända exemplaret till den som ringer. 209 00:12:55,740 --> 00:12:59,840 >> Vem visste sådan enkel till synes funktion så kan vara komplicerat? 210 00:12:59,840 --> 00:13:02,820 Nu vet du vad som händer i CS50 biblioteket. 211 00:13:02,820 --> 00:13:06,470 >> Mitt namn är Nate Hardison, och detta är CS50. 212 00:13:06,470 --> 00:13:08,350 [CS50.TV]