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 [Dette er CS50. CS50.TV] 4 00:00:07,260 --> 00:00:11,510 Den CS50 biblioteket er et nyttig verktøy som vi har installert på maskinen 5 00:00:11,510 --> 00:00:15,870 å gjøre det enklere for deg å skrive programmer som ber brukerne for innspill. 6 00:00:15,870 --> 00:00:21,670 I denne videoen, vil vi trekke tilbake gardin og se på hva er i CS50 biblioteket. 7 00:00:21,670 --> 00:00:25,520 >> I videoen på C-biblioteker, snakker vi om hvordan du # include overskrifter filer 8 00:00:25,520 --> 00:00:27,570 av biblioteket i kildekoden, 9 00:00:27,570 --> 00:00:31,150 og deretter koble med en binær bibliotekfil under knytte fase 10 00:00:31,150 --> 00:00:33,140 av innsamlingsprosessen. 11 00:00:33,140 --> 00:00:36,440 Header-filene spesifisere grensesnittet av biblioteket. 12 00:00:36,440 --> 00:00:41,280 Det vil si, de detalj alle de ressursene som biblioteket har tilgjengelig for deg å bruke, 13 00:00:41,280 --> 00:00:45,250 som funksjon erklæringer, konstanter og datatyper. 14 00:00:45,250 --> 00:00:48,890 Den binære bibliotekfil inneholder gjennomføringen av biblioteket, 15 00:00:48,890 --> 00:00:54,580 som er satt sammen av bibliotekets header-filer og bibliotekets. c kildekodefiler. 16 00:00:54,580 --> 00:00:59,820 >> Den binære bibliotekfilen er ikke veldig interessant å se på siden det er, vel, i binær. 17 00:00:59,820 --> 00:01:03,300 Så, la oss ta en titt på header filer for biblioteket i stedet. 18 00:01:03,300 --> 00:01:07,710 I dette tilfellet er det bare én header fil som heter cs50.h. 19 00:01:07,710 --> 00:01:11,040 Vi har installert den i brukerens inkluderer katalogen 20 00:01:11,040 --> 00:01:15,150 sammen med de andre systemene bibliotekenes header filer. 21 00:01:15,150 --> 00:01:21,530 >> En av de første tingene du vil legge merke til er at cs50.h # inkluderer header-filer fra andre bibliotek - 22 00:01:21,530 --> 00:01:25,670 float, grenser, standard bool og standard lib. 23 00:01:25,670 --> 00:01:28,800 Igjen, etter prinsippet om ikke gjenoppfinne hjulet, 24 00:01:28,800 --> 00:01:33,490 Vi har bygget CS0 bibliotek ved hjelp av verktøy som andre gitt til oss. 25 00:01:33,490 --> 00:01:38,690 >> Den neste tingen du vil se i biblioteket er at vi definerer en ny type som heter "string". 26 00:01:38,690 --> 00:01:42,330 Denne linjen egentlig bare skaper et alias for char * type, 27 00:01:42,330 --> 00:01:46,000 slik at den ikke tilegner magisk den nye strengen type med attributter 28 00:01:46,000 --> 00:01:49,650 vanligvis forbindes med streng objekter i andre språk, 29 00:01:49,650 --> 00:01:50,850 for eksempel lengden. 30 00:01:50,850 --> 00:01:55,180 Grunnen til at vi har gjort dette på er å skjerme nye programmerere fra blodig detaljer 31 00:01:55,180 --> 00:01:57,580 av pekere til de er klare. 32 00:01:57,580 --> 00:02:00,130 >> Den neste delen av topptekstfilen er erklæringen av funksjonene 33 00:02:00,130 --> 00:02:04,410 at CS50 biblioteket gir sammen med dokumentasjon. 34 00:02:04,410 --> 00:02:06,940 Legg merke til detaljnivået i kommentarfeltet her. 35 00:02:06,940 --> 00:02:10,560 Dette er super viktig, slik at folk vet hvordan de skal bruke disse funksjonene. 36 00:02:10,560 --> 00:02:19,150 Vi erklærer i sin tur fungerer å spørre brukeren og retur tegn, dobbeltrom, flyter, ints, 37 00:02:19,150 --> 00:02:24,160 lang lengter og strenger, med vår egen streng type. 38 00:02:24,160 --> 00:02:26,260 Etter prinsippet om informasjon skjule, 39 00:02:26,260 --> 00:02:31,640 Vi har satt vår definisjon i en egen c implementering fil -. cs50.c-- 40 00:02:31,640 --> 00:02:35,110 ligger i brukerens kilde katalogen. 41 00:02:35,110 --> 00:02:38,040 Vi har forutsatt at filen slik at du kan ta en titt på det, 42 00:02:38,040 --> 00:02:41,490 lære av det, og rekompilere det på forskjellige maskiner hvis du ønsker, 43 00:02:41,490 --> 00:02:45,510 selv om vi tror det er bedre å jobbe på apparatet for denne klassen. 44 00:02:45,510 --> 00:02:47,580 Uansett, la oss ta en titt på det nå. 45 00:02:49,020 --> 00:02:54,620 >> Funksjonene getchar, GetDouble, GetFloat, GetInt, og GetLongLong 46 00:02:54,620 --> 00:02:58,160 er alle bygget på toppen av GetString funksjon. 47 00:02:58,160 --> 00:03:01,510 Det viser seg at de alle følger i hovedsak samme mønster. 48 00:03:01,510 --> 00:03:04,870 De bruker en stund loop å be brukeren om en linje av input. 49 00:03:04,870 --> 00:03:08,430 De kommer tilbake en spesiell verdi hvis brukeren skriver inn en tom linje. 50 00:03:08,430 --> 00:03:11,750 De forsøker å analysere brukerens input som den aktuelle typen, 51 00:03:11,750 --> 00:03:15,010 det være seg en røye, en dobbel, en flåte, etc. 52 00:03:15,010 --> 00:03:18,710 Og så de enten returnere resultatet hvis input var vellykket analysert 53 00:03:18,710 --> 00:03:21,330 eller de reprompt brukeren. 54 00:03:21,330 --> 00:03:24,230 >> På et høyt nivå, det er ingenting virkelig vanskelig her. 55 00:03:24,230 --> 00:03:28,760 Du har kanskje skrevet tilsvarende strukturert koden selv i det siste. 56 00:03:28,760 --> 00:03:34,720 Kanskje den mest kryptiske utseende delen er sscanf samtalen som analyserer brukerens input. 57 00:03:34,720 --> 00:03:38,160 Sscanf tilhører inndataformatet konvertering familie. 58 00:03:38,160 --> 00:03:42,300 Den lever i standard io.h, og dens oppgave er å analysere en C streng, 59 00:03:42,300 --> 00:03:46,520 ifølge et bestemt format, lagring parse fører variabel 60 00:03:46,520 --> 00:03:48,720 gitt av den som ringer. 61 00:03:48,720 --> 00:03:53,570 Siden inndataformat konvertering funksjoner er svært nyttige, mye brukte funksjoner 62 00:03:53,570 --> 00:03:56,160 som ikke er super ved første øyekast, 63 00:03:56,160 --> 00:03:58,300 vi vil gå over hvordan sscanf fungerer. 64 00:03:58,300 --> 00:04:03,330 >> Det første argumentet til sscanf er en char * - en peker til et tegn. 65 00:04:03,330 --> 00:04:05,150 For funksjonen skal fungere skikkelig, 66 00:04:05,150 --> 00:04:08,340 at karakter skal være det første tegnet i en C streng, 67 00:04:08,340 --> 00:04:12,270 avsluttet med null \ 0 karakter. 68 00:04:12,270 --> 00:04:15,120 Dette er strengen til å analysere 69 00:04:15,120 --> 00:04:18,269 Det andre argumentet til sscanf er et format streng, 70 00:04:18,269 --> 00:04:20,839 typisk gått inn som en streng konstant, 71 00:04:20,839 --> 00:04:24,040 og du har kanskje sett en streng som dette før når du bruker printf. 72 00:04:24,040 --> 00:04:28,650 En prosent skilt i formatstrengen viser en konvertering Specifier. 73 00:04:28,650 --> 00:04:30,850 Tegnet umiddelbart etter et prosenttegn, 74 00:04:30,850 --> 00:04:35,430 indikerer C-typen som vi ønsker sscanf å konvertere til. 75 00:04:35,430 --> 00:04:40,090 I GetInt, ser du at det er en% d og% c. 76 00:04:40,090 --> 00:04:48,690 Dette betyr at sscanf vil prøve å en desimal int - den% d - og røye - den% c. 77 00:04:48,690 --> 00:04:51,510 For hver konvertering Specifier i formatstrengen, 78 00:04:51,510 --> 00:04:56,620 sscanf forventer en tilsvarende argument senere i sin argumentasjon listen. 79 00:04:56,620 --> 00:05:00,850 Som argument må peke til et hensiktsmessig skrevet beliggenhet 80 00:05:00,850 --> 00:05:04,000 der du vil lagre resultatet av konverteringen. 81 00:05:04,000 --> 00:05:08,910 >> Den typiske måten å gjøre dette på er å opprette en variabel på stakken før sscanf samtalen 82 00:05:08,910 --> 00:05:11,440 for hvert element du ønsker å analysere fra strengen 83 00:05:11,440 --> 00:05:15,520 og deretter bruke adressen operatør - tegnet - å passere pekere 84 00:05:15,520 --> 00:05:19,100 til disse variablene til sscanf samtalen. 85 00:05:19,100 --> 00:05:22,720 Du kan se at i GetInt gjør vi akkurat dette. 86 00:05:22,720 --> 00:05:28,240 Rett før sscanf samtalen, erklærer vi en int kalt n og en røye samtale c på stakken, 87 00:05:28,240 --> 00:05:32,340 og vi passerer pekere til dem i sscanf samtalen. 88 00:05:32,340 --> 00:05:35,800 Sette disse variablene på stakken er å foretrekke fremfor å bruke plass tildelt 89 00:05:35,800 --> 00:05:39,350 på haugen med malloc, siden du unngå overhead av malloc samtalen, 90 00:05:39,350 --> 00:05:43,060 og du trenger ikke å bekymre deg for lekker minne. 91 00:05:43,060 --> 00:05:47,280 Tegn som ikke innledes med et prosenttegn ikke be konvertering. 92 00:05:47,280 --> 00:05:50,380 Snarere er de bare legge til filformatet. 93 00:05:50,380 --> 00:05:56,500 >> For eksempel, hvis formatstreng GetInt var% d i stedet, 94 00:05:56,500 --> 00:05:59,800 sscanf ville se etter bokstaven a etterfulgt av en int, 95 00:05:59,800 --> 00:06:04,360 og mens det ville forsøke å konvertere int, ville det ikke gjøre noe annet med en. 96 00:06:04,360 --> 00:06:07,440 Det eneste unntaket til dette er mellomrom. 97 00:06:07,440 --> 00:06:11,030 Hvite mellomrom i formatstrengen matche ethvert mengde blanktegn - 98 00:06:11,030 --> 00:06:12,890 selv ingen. 99 00:06:12,890 --> 00:06:18,100 Så, det er grunnen til at kommentaren nevner muligens med ledende og / eller etterfølgende mellomrom. 100 00:06:18,100 --> 00:06:22,910 Så vil på dette punktet ser det ut som vår sscanf samtale forsøker å analysere brukerens input streng 101 00:06:22,910 --> 00:06:25,380 ved å se etter mulige ledende mellomrom, 102 00:06:25,380 --> 00:06:29,300 etterfulgt av en int som vil bli konvertert og lagret i int variabel n 103 00:06:29,300 --> 00:06:33,090 etterfulgt av en viss grad av blanke, og etterfulgt av et tegn 104 00:06:33,090 --> 00:06:35,810 lagret i char variabelen c.. 105 00:06:35,810 --> 00:06:37,790 >> Hva om returverdien? 106 00:06:37,790 --> 00:06:41,560 Sscanf vil analysere inndataene linje fra start til slutt, 107 00:06:41,560 --> 00:06:44,860 stopper når den når slutten eller når et tegn i input 108 00:06:44,860 --> 00:06:49,320 samsvarer ikke et format tegn eller når det ikke kan gjøre en konvertering. 109 00:06:49,320 --> 00:06:52,690 Det er returverdi brukes til å skille når det stoppet. 110 00:06:52,690 --> 00:06:55,670 Hvis det stoppet, fordi det nådd slutten av inndatastrengen 111 00:06:55,670 --> 00:07:00,630 før du gjør noen konverteringer og før unnlate å søke på en del av formatstrengen, 112 00:07:00,630 --> 00:07:04,840 deretter spesielt konstant EOF er returnert. 113 00:07:04,840 --> 00:07:08,200 Ellers returnerer den antall vellykkede konverteringer, 114 00:07:08,200 --> 00:07:14,380 som kan være 0, 1 eller 2, siden vi har bedt om to konverteringer. 115 00:07:14,380 --> 00:07:19,000 I vårt tilfelle ønsker vi å sørge for at brukeren har skrevet i en int og bare en int. 116 00:07:19,000 --> 00:07:23,370 >> Så ønsker vi sscanf å returnere en. Se hvorfor? 117 00:07:23,370 --> 00:07:26,850 Hvis sscanf tilbake 0, så vil ingen konverteringer ble gjort, 118 00:07:26,850 --> 00:07:31,690 så brukeren har skrevet noe annet enn en int på begynnelsen av input. 119 00:07:31,690 --> 00:07:37,100 Hvis sscanf returnerer 2, så brukeren gjorde riktig skriv det i ved begynnelsen av tilførselen 120 00:07:37,100 --> 00:07:41,390 men de da skrevet i noen ikke-mellomrom karakter etterpå 121 00:07:41,390 --> 00:07:44,940 siden% c konvertering lyktes. 122 00:07:44,940 --> 00:07:49,570 Wow, det er ganske lang forklaring for én funksjon samtale. 123 00:07:49,570 --> 00:07:53,460 Uansett, hvis du ønsker mer informasjon om sscanf og sine søsken, 124 00:07:53,460 --> 00:07:57,130 sjekk ut mannen sidene, Google, eller begge deler. 125 00:07:57,130 --> 00:07:58,780 Det er mange strengformateringsangrep alternativer, 126 00:07:58,780 --> 00:08:03,830 og disse kan spare deg for mye manuelt arbeid når du prøver å analysere strenger i C. 127 00:08:03,830 --> 00:08:07,180 >> Den siste funksjon i biblioteket for å se på er GetString. 128 00:08:07,180 --> 00:08:10,310 Det viser seg at GetString er en vanskelig funksjon å skrive riktig, 129 00:08:10,310 --> 00:08:14,290 selv om det virker som en enkel, felles oppgave. 130 00:08:14,290 --> 00:08:16,170 Hvorfor er det slik? 131 00:08:16,170 --> 00:08:21,380 Vel, la oss tenke på hvordan vi skal lagre linjen som brukeren skriver i. 132 00:08:21,380 --> 00:08:23,880 Siden en streng er en sekvens av tegn, 133 00:08:23,880 --> 00:08:26,430 vi kanskje ønsker å lagre den i en matrise på stakken, 134 00:08:26,430 --> 00:08:31,250 men vi trenger å vite hvor lenge rekken kommer til å være når vi erklærer den. 135 00:08:31,250 --> 00:08:34,030 Likeledes, hvis vi ønsker å sette det på haugen, 136 00:08:34,030 --> 00:08:38,090 Vi må passere for å malloc antall byte vi ønsker å reservere, 137 00:08:38,090 --> 00:08:39,730 men dette er umulig. 138 00:08:39,730 --> 00:08:42,760 Vi har ingen anelse om hvor mange tegn brukeren vil skrive 139 00:08:42,760 --> 00:08:46,590 før brukeren faktisk ikke skriver dem. 140 00:08:46,590 --> 00:08:50,720 >> En naiv løsning på dette problemet er å bare bestille en stor del av plassen, sier 141 00:08:50,720 --> 00:08:54,540 en blokk på 1000 tegn for brukerens inngang, 142 00:08:54,540 --> 00:08:57,980 forutsatt at brukeren aldri ville skrive i en streng så lenge. 143 00:08:57,980 --> 00:09:00,810 Dette er en dårlig idé for to grunner. 144 00:09:00,810 --> 00:09:05,280 Først, forutsatt at brukerne vanligvis ikke skriver i strenger så lenge, 145 00:09:05,280 --> 00:09:07,610 du kan kaste bort en masse minne. 146 00:09:07,610 --> 00:09:10,530 På moderne maskiner, kan dette ikke være et problem hvis du gjør dette 147 00:09:10,530 --> 00:09:13,890 i en eller to isolerte tilfeller, 148 00:09:13,890 --> 00:09:17,630 men hvis du tar brukerens input i en sløyfe og lagring for senere bruk, 149 00:09:17,630 --> 00:09:20,870 du raskt kan suge opp massevis av minne. 150 00:09:20,870 --> 00:09:24,450 I tillegg, hvis programmet du skriver er for en mindre maskin - 151 00:09:24,450 --> 00:09:28,100 en enhet som en smarttelefon eller noe annet med begrenset minne - 152 00:09:28,100 --> 00:09:32,060 denne løsningen vil skape problemer mye raskere. 153 00:09:32,060 --> 00:09:36,450 Den andre, mer alvorlig grunn til å ikke gjøre dette er at det etterlater programmet sårbar 154 00:09:36,450 --> 00:09:39,710 til det som kalles en buffer overflow angrep. 155 00:09:39,710 --> 00:09:45,840 I programmering, er et bufferminne som brukes til å midlertidig lagre inn-eller utgang data, 156 00:09:45,840 --> 00:09:48,980 som i dette tilfellet er vår 1000-char blokk. 157 00:09:48,980 --> 00:09:53,370 En buffer overflow oppstår når data skrives forbi slutten av blokken. 158 00:09:53,370 --> 00:09:57,790 >> For eksempel, hvis en bruker faktisk gjør type i mer enn 1000 tegn. 159 00:09:57,790 --> 00:10:01,570 Du har kanskje opplevd dette ved et uhell når du programmerer med arrays. 160 00:10:01,570 --> 00:10:05,620 Hvis du har en rekke 10 ints, stopper ingenting deg fra å prøve å lese eller skrive 161 00:10:05,620 --> 00:10:07,810 15. int. 162 00:10:07,810 --> 00:10:10,000 Det er ingen kompilatoren advarsler eller feil. 163 00:10:10,000 --> 00:10:13,250 Programmet bare tabber rett fram og åpner minne 164 00:10:13,250 --> 00:10:18,150 hvor den tror den 15. int vil bli, og dette kan overskrive de andre variablene. 165 00:10:18,150 --> 00:10:22,040 I verste fall kan du overskrive noen av programmets interne 166 00:10:22,040 --> 00:10:26,820 kontrollmekanismer, forårsaker programmet faktisk utføre forskjellige instruksjoner 167 00:10:26,820 --> 00:10:28,340 enn du hadde tenkt. 168 00:10:28,340 --> 00:10:31,360 >> Nå er det ikke vanlig å gjøre dette ved et uhell, 169 00:10:31,360 --> 00:10:35,150 men dette er en ganske vanlig teknikk som skurkene bruker til å bryte programmer 170 00:10:35,150 --> 00:10:39,080 og sette ondsinnet kode på andres datamaskiner. 171 00:10:39,080 --> 00:10:42,910 Derfor kan vi ikke bare bruke vår naiv løsning. 172 00:10:42,910 --> 00:10:45,590 Vi trenger en måte å hindre at våre programmer fra å være sårbar 173 00:10:45,590 --> 00:10:47,880 til en buffer overflow angrep. 174 00:10:47,880 --> 00:10:51,430 For å gjøre dette, må vi sørge for at våre buffer kan vokse som vi leser 175 00:10:51,430 --> 00:10:53,850 mer input fra brukeren. 176 00:10:53,850 --> 00:10:57,440 Løsningen? Vi bruker en haug tilordnede bufferen. 177 00:10:57,440 --> 00:10:59,950 Siden vi kan endre størrelsen med resize den RealLOC funksjon, 178 00:10:59,950 --> 00:11:04,580 og vi holde styr av to tall - indeksen for neste tomme sporet i bufferen 179 00:11:04,580 --> 00:11:08,390 og lengden eller kapasiteten på bufferen. 180 00:11:08,390 --> 00:11:13,210 Vi leser i chars fra brukeren en om gangen ved hjelp av fgetc funksjonen. 181 00:11:13,210 --> 00:11:19,360 Argumentet den fgetc funksjonen tar - stdin - er en referanse til standard input strengen, 182 00:11:19,360 --> 00:11:23,810 som er en forhåndskobles inngangskanal som brukes til å overføre brukerens input 183 00:11:23,810 --> 00:11:26,270 fra terminalen til programmet. 184 00:11:26,270 --> 00:11:29,890 >> Når brukeren skriver inn et nytt tegn, sjekker vi for å se om indeksen 185 00:11:29,890 --> 00:11:35,810 av den neste ledige sporet pluss 1 er større enn kapasiteten av bufferen. 186 00:11:35,810 --> 00:11:39,690 Den en kommer inn fordi om den neste gratis indeksen er 5, 187 00:11:39,690 --> 00:11:44,150 da vår buffer lengde må være 6 takket være 0 indeksering. 188 00:11:44,150 --> 00:11:48,350 Hvis vi har kjørt tom for plass i bufferen, så vi forsøker å endre størrelsen, 189 00:11:48,350 --> 00:11:51,690 dobling det slik at vi kutte ned på antall ganger vi størrelsen 190 00:11:51,690 --> 00:11:54,760 hvis brukeren er å skrive i en veldig lang streng. 191 00:11:54,760 --> 00:11:57,950 Hvis strengen har blitt for lang, eller hvis vi går tom for heap minne, 192 00:11:57,950 --> 00:12:01,350 vi frigjøre vår buffer og avkastning null. 193 00:12:01,350 --> 00:12:04,170 >> Endelig, føyer vi char til bufferen. 194 00:12:04,170 --> 00:12:08,200 Når brukeren treff Enter eller Retur, signaliserer en ny linje, 195 00:12:08,200 --> 00:12:12,050 eller spesielle tegn - kontroll d - som signaliserer en slutt inngang, 196 00:12:12,050 --> 00:12:16,240 vi gjør en sjekk for å se om brukeren faktisk skrevet i noe som helst. 197 00:12:16,240 --> 00:12:18,820 Hvis ikke, går vi tilbake null. 198 00:12:18,820 --> 00:12:22,280 Ellers, fordi vår buffer er trolig større enn vi trenger, 199 00:12:22,280 --> 00:12:24,830 i verste fall er det nesten dobbelt så stor som vi trenger 200 00:12:24,830 --> 00:12:27,830 siden vi doble hver gang vi endrer størrelsen, 201 00:12:27,830 --> 00:12:31,840 vi gjør et nytt eksemplar av strengen ved hjelp av bare hvor mye plass som vi trenger. 202 00:12:31,840 --> 00:12:34,220 Vi legger til en ekstra 1 til malloc samtalen, 203 00:12:34,220 --> 00:12:37,810 slik at det er plass til den spesielle null terminator tegn - \ 0, 204 00:12:37,810 --> 00:12:41,990 som vi føyer til strengen når vi kopierer i resten av tegnene, 205 00:12:41,990 --> 00:12:45,060 hjelp strncpy stedet for strcpy 206 00:12:45,060 --> 00:12:48,830 slik at vi kan spesifisere nøyaktig hvor mange tegn vi ønsker å kopiere. 207 00:12:48,830 --> 00:12:51,690 Strcpy kopier før den treffer en \ 0. 208 00:12:51,690 --> 00:12:55,740 Da vi frigjøre vår buffer og returnere kopi til den som ringer. 209 00:12:55,740 --> 00:12:59,840 >> Hvem visste for eksempel en enkel-tilsynelatende funksjon kan være så komplisert? 210 00:12:59,840 --> 00:13:02,820 Nå vet du hva som går inn i CS50 biblioteket. 211 00:13:02,820 --> 00:13:06,470 >> Mitt navn er Nate Hardison, og dette er CS50. 212 00:13:06,470 --> 00:13:08,350 [CS50.TV]