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 Det CS50 Biblioteket er et nyttigt værktøj, som vi har installeret på apparatet 5 00:00:11,510 --> 00:00:15,870 at gøre det lettere for dig at skrive programmer, der ansporer brugere til input. 6 00:00:15,870 --> 00:00:21,670 I denne video, vil vi trække sig tilbage gardinet og se på, hvad der præcist er i CS50 bibliotek. 7 00:00:21,670 --> 00:00:25,520 >> I videoen på C-biblioteker, taler vi om, hvordan du # include headers filer 8 00:00:25,520 --> 00:00:27,570 af biblioteket i din kildekode, 9 00:00:27,570 --> 00:00:31,150 og derefter du linker med en binær biblioteksfil i sammenkædningen fase 10 00:00:31,150 --> 00:00:33,140 af fastlæggelsen processen. 11 00:00:33,140 --> 00:00:36,440 De header-filer angive interfacet af biblioteket. 12 00:00:36,440 --> 00:00:41,280 Det vil sige, at de detaljer alle de ressourcer, biblioteket har til rådighed for dig at bruge, 13 00:00:41,280 --> 00:00:45,250 ligesom funktionserklæringer, konstanter og datatyper. 14 00:00:45,250 --> 00:00:48,890 Den binære bibliotek fil indeholder gennemførelsen af ​​biblioteket, 15 00:00:48,890 --> 00:00:54,580 som er udarbejdet på grundlag af bibliotekets header-filer og bibliotekets. C kildekodefiler. 16 00:00:54,580 --> 00:00:59,820 >> Den binære biblioteksfil er ikke meget interessant at se på, da det er, ja, i binær. 17 00:00:59,820 --> 00:01:03,300 Så lad os tage et kig på header-filer til biblioteket i stedet. 18 00:01:03,300 --> 00:01:07,710 I dette tilfælde er der kun en header fil kaldet cs50.h. 19 00:01:07,710 --> 00:01:11,040 Vi har installeret det i brugerens omfatter biblioteket 20 00:01:11,040 --> 00:01:15,150 sammen med de andre systemkomponenter bibliotekernes header-filer. 21 00:01:15,150 --> 00:01:21,530 >> En af de første ting, du lægger mærke til, er, at cs50.h # inkluderer header-filer fra andre biblioteker - 22 00:01:21,530 --> 00:01:25,670 float, grænser, standard bool og standard lib. 23 00:01:25,670 --> 00:01:28,800 Igen, efter princippet om ikke at genopfinde hjulet, 24 00:01:28,800 --> 00:01:33,490 vi har opbygget den CS0 biblioteket ved hjælp af værktøjer som andre, der er fastsat for os. 25 00:01:33,490 --> 00:01:38,690 >> Den næste ting du vil se i biblioteket er, at vi definere en ny type kaldet "streng". 26 00:01:38,690 --> 00:01:42,330 Denne linje virkelig bare skaber et alias for den char * type, 27 00:01:42,330 --> 00:01:46,000 så det ikke magisk gennemsyre den nye streng type med attributter 28 00:01:46,000 --> 00:01:49,650 ofte forbundet med strengeobjekter på andre sprog, 29 00:01:49,650 --> 00:01:50,850 såsom længde. 30 00:01:50,850 --> 00:01:55,180 Grunden til at vi har gjort dette, er at beskytte nye programmører fra de blodige detaljer 31 00:01:55,180 --> 00:01:57,580 af pegepinde, indtil de er klar. 32 00:01:57,580 --> 00:02:00,130 >> Den næste del af header filen er erklæringen af ​​funktionerne 33 00:02:00,130 --> 00:02:04,410 at CS50 Biblioteket sammen med dokumentation. 34 00:02:04,410 --> 00:02:06,940 Læg mærke til detaljeringsgraden i kommentarerne her. 35 00:02:06,940 --> 00:02:10,560 Det er super vigtigt, så folk ved, hvordan man bruger disse funktioner. 36 00:02:10,560 --> 00:02:19,150 Vi erklærer til gengæld fungerer til at bede brugeren og retur chars, doubler, flåd, int'er, 37 00:02:19,150 --> 00:02:24,160 lang længes, og strygere, ved hjælp af vores egen streng type. 38 00:02:24,160 --> 00:02:26,260 Efter princippet om information skjul, 39 00:02:26,260 --> 00:02:31,640 Vi har sat vores definition i en separat c implementering fil -. cs50.c-- 40 00:02:31,640 --> 00:02:35,110 placeret i brugerens kildebiblioteket. 41 00:02:35,110 --> 00:02:38,040 Vi har forudsat at fil, så du kan tage et kig på det, 42 00:02:38,040 --> 00:02:41,490 lære af det, og kompilere den på forskellige maskiner, hvis du ønsker, 43 00:02:41,490 --> 00:02:45,510 selv om vi synes, det er bedre at arbejde på apparatet for denne klasse. 44 00:02:45,510 --> 00:02:47,580 Anyway, lad os tage et kig på det nu. 45 00:02:49,020 --> 00:02:54,620 >> Funktionerne getchar, GetDouble, GetFloat, GetInt, og GetLongLong 46 00:02:54,620 --> 00:02:58,160 er alle indbygget oven på GetString funktion. 47 00:02:58,160 --> 00:03:01,510 Det viser sig, at de følger alle væsentlige samme mønster. 48 00:03:01,510 --> 00:03:04,870 De bruger en while-løkke til at bede brugeren om en linje af input. 49 00:03:04,870 --> 00:03:08,430 De vender tilbage en særlig værdi, hvis brugeren indtaster en tom linje. 50 00:03:08,430 --> 00:03:11,750 De forsøger at parse brugerens input som den relevante type, 51 00:03:11,750 --> 00:03:15,010 det være sig en char, en dobbelt, en float osv. 52 00:03:15,010 --> 00:03:18,710 Og så de enten returnere resultatet, hvis inputtet er fuldført parset 53 00:03:18,710 --> 00:03:21,330 eller de reprompt brugeren. 54 00:03:21,330 --> 00:03:24,230 >> På et højt niveau, er der ikke noget virkelig svært her. 55 00:03:24,230 --> 00:03:28,760 Du har måske skrevet tilsvarende strukturerede koden selv i fortiden. 56 00:03:28,760 --> 00:03:34,720 Måske den mest kryptiske udseende del er den sscanf opkald, der analyserer brugerens indlæsning. 57 00:03:34,720 --> 00:03:38,160 Sscanf er en del af input formatkonvertering familien. 58 00:03:38,160 --> 00:03:42,300 Den lever i standard io.h, og dens opgave er at parse en C streng, 59 00:03:42,300 --> 00:03:46,520 efter et bestemt format, at lagre parse resulterer i variable 60 00:03:46,520 --> 00:03:48,720 tilvejebringes af den opkaldende. 61 00:03:48,720 --> 00:03:53,570 Da det indtastede format konvertering funktioner er meget nyttige, almindeligt anvendte funktioner 62 00:03:53,570 --> 00:03:56,160 der er ikke super intuitive i starten, 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 argument til sscanf er en char * - en pegepind til et tegn. 65 00:04:03,330 --> 00:04:05,150 For at funktionen skal fungere korrekt, 66 00:04:05,150 --> 00:04:08,340 denne karakter bør være det første tegn på en C-streng, 67 00:04:08,340 --> 00:04:12,270 termineres med nul \ 0 tegn. 68 00:04:12,270 --> 00:04:15,120 Dette er streng at parse 69 00:04:15,120 --> 00:04:18,269 Det andet argument til sscanf er en formatstreng, 70 00:04:18,269 --> 00:04:20,839 typisk bestået i som en streng konstant, 71 00:04:20,839 --> 00:04:24,040 og du måske har set en streng som dette før, når du bruger printf. 72 00:04:24,040 --> 00:04:28,650 Et procenttegn i formatet streng viser en konvertering Projekteringsvejledning. 73 00:04:28,650 --> 00:04:30,850 Tegnet umiddelbart efter et procenttegn, 74 00:04:30,850 --> 00:04:35,430 angiver type C, som vi ønsker sscanf at konvertere til. 75 00:04:35,430 --> 00:04:40,090 I GetInt, se dig, at der er en% d og en% c. 76 00:04:40,090 --> 00:04:48,690 Det betyder, at sscanf vil forsøge at en decimal int - den% d - og en char - den% c. 77 00:04:48,690 --> 00:04:51,510 For hver konvertering Projekteringsvejledning i det format string, 78 00:04:51,510 --> 00:04:56,620 sscanf forventer en tilsvarende argument senere i sin argumentation liste. 79 00:04:56,620 --> 00:05:00,850 Dette argument må pege på en passende indtastet placering 80 00:05:00,850 --> 00:05:04,000 hvor at lagre resultatet af omdannelsen. 81 00:05:04,000 --> 00:05:08,910 >> Den typiske måde at gøre dette på er at oprette en variabel på stakken før sscanf opkald 82 00:05:08,910 --> 00:05:11,440 for hvert element, du ønsker at tolke fra strengen 83 00:05:11,440 --> 00:05:15,520 og derefter bruge den adresse operatør - tegnet - at passere pointers 84 00:05:15,520 --> 00:05:19,100 disse variabler til sscanf opkaldet. 85 00:05:19,100 --> 00:05:22,720 Du kan se, at i GetInt vi gøre netop dette. 86 00:05:22,720 --> 00:05:28,240 Lige før sscanf opkald, erklærer vi en int kaldet n og en char opkald c på stakken, 87 00:05:28,240 --> 00:05:32,340 og vi passerer henvisninger til dem i sscanf opkald. 88 00:05:32,340 --> 00:05:35,800 Sætte disse variabler på stakken er foretrukket frem for hjælp tildelt plads 89 00:05:35,800 --> 00:05:39,350 på heapen med malloc, da du undgå overhead af malloc opkald, 90 00:05:39,350 --> 00:05:43,060 og du behøver ikke at bekymre dig om utæt hukommelse. 91 00:05:43,060 --> 00:05:47,280 Tegn ikke indledt med et procenttegn ikke bede konvertering. 92 00:05:47,280 --> 00:05:50,380 Snarere de bare føje til formatet specifikationen. 93 00:05:50,380 --> 00:05:56,500 >> For eksempel, hvis det format string i GetInt var% d stedet 94 00:05:56,500 --> 00:05:59,800 sscanf ville kigge efter bogstavet a efterfulgt af en int, 95 00:05:59,800 --> 00:06:04,360 og mens det vil forsøge at konvertere int, ville det ikke gøre noget andet med en. 96 00:06:04,360 --> 00:06:07,440 Den eneste undtagelse til dette er mellemrum. 97 00:06:07,440 --> 00:06:11,030 Blanktegn i formatstreng matche enhver mængde whitespace - 98 00:06:11,030 --> 00:06:12,890 selv slet ingen. 99 00:06:12,890 --> 00:06:18,100 Så det er grunden til, at kommentaren nævner muligvis med førende og / eller afsluttende blanke. 100 00:06:18,100 --> 00:06:22,910 Så vil på dette tidspunkt det ligner vores sscanf opkald forsøge at parse brugerens input streng 101 00:06:22,910 --> 00:06:25,380 ved at kontrollere for eventuelle ledende mellemrum, 102 00:06:25,380 --> 00:06:29,300 efterfulgt af en int, der konverteres og gemmes i int Variablen n 103 00:06:29,300 --> 00:06:33,090 efterfulgt af en vis mængde af mellemrum, og efterfulgt af et tegn 104 00:06:33,090 --> 00:06:35,810 lagret i den forkullede masse variable ca. 105 00:06:35,810 --> 00:06:37,790 >> Hvad med den returnerede værdi? 106 00:06:37,790 --> 00:06:41,560 Sscanf vil parse input linje fra start til slut, 107 00:06:41,560 --> 00:06:44,860 stopper, når den når slutningen, eller når et tegn i input 108 00:06:44,860 --> 00:06:49,320 matcher ikke et format forandringer, eller når det ikke kan lave en konvertering. 109 00:06:49,320 --> 00:06:52,690 Det er returværdien bruges til at fremhæve, når det stoppet. 110 00:06:52,690 --> 00:06:55,670 Hvis det stoppes, da den nåede enden af ​​input string 111 00:06:55,670 --> 00:07:00,630 før du foretager nogen konverteringer og før undlade at matche en del af formatstreng, 112 00:07:00,630 --> 00:07:04,840 så den særlige konstant EOF returneres. 113 00:07:04,840 --> 00:07:08,200 Ellers er det returnerer antallet af gennemførte konverteringer, 114 00:07:08,200 --> 00:07:14,380 som kunne være 0, 1 eller 2, da vi har bedt om to konverteringer. 115 00:07:14,380 --> 00:07:19,000 I vores tilfælde vil vi sørge for, at brugeren har indtastet i en int, og kun en int. 116 00:07:19,000 --> 00:07:23,370 >> Så vi ønsker sscanf at returnere 1. Se hvorfor? 117 00:07:23,370 --> 00:07:26,850 Hvis sscanf returnerede 0, så ingen konverteringer blev foretaget, 118 00:07:26,850 --> 00:07:31,690 så brugeren har indtastet noget andet end en int ved begyndelsen af ​​den indgående. 119 00:07:31,690 --> 00:07:37,100 Hvis sscanf returnerer 2, så brugeren ikke korrekt skrive det i begyndelsen af ​​input, 120 00:07:37,100 --> 00:07:41,390 men de så skrevet i nogle ikke-hvidt tegn bagefter 121 00:07:41,390 --> 00:07:44,940 eftersom% c omdannelse lykkedes. 122 00:07:44,940 --> 00:07:49,570 Wow, det er en ganske lang forklaring til én funktion opkald. 123 00:07:49,570 --> 00:07:53,460 Anyway, hvis du ønsker mere information om sscanf og sine søskende, 124 00:07:53,460 --> 00:07:57,130 tjek de man-siderne, Google, eller begge dele. 125 00:07:57,130 --> 00:07:58,780 Der er masser af formatstrengssårbarheder muligheder, 126 00:07:58,780 --> 00:08:03,830 og disse kan spare dig for en masse manuelt arbejde, når de forsøger at parse strenge i C. 127 00:08:03,830 --> 00:08:07,180 >> Den endelige funktion i biblioteket for at se på, er GetString. 128 00:08:07,180 --> 00:08:10,310 Det viser sig, at GetString er en vanskelig funktion til at skrive ordentligt, 129 00:08:10,310 --> 00:08:14,290 selvom det virker som sådan en simpel, fælles opgave. 130 00:08:14,290 --> 00:08:16,170 Hvorfor er det sådan? 131 00:08:16,170 --> 00:08:21,380 Nå, lad os tænke over, hvordan vi skal gemme den linje, som brugeren skriver i. 132 00:08:21,380 --> 00:08:23,880 Da en streng er en sekvens af tegn, 133 00:08:23,880 --> 00:08:26,430 vi måske ønsker at gemme det i et array på stakken, 134 00:08:26,430 --> 00:08:31,250 men vi ville få brug for at vide, hvor længe sættet vil være, når vi erklære den. 135 00:08:31,250 --> 00:08:34,030 Ligeledes, hvis vi ønsker at sætte det på bunke, 136 00:08:34,030 --> 00:08:38,090 vi er nødt til at passere til malloc antallet af bytes vi ønsker at reservere, 137 00:08:38,090 --> 00:08:39,730 men dette er umuligt. 138 00:08:39,730 --> 00:08:42,760 Vi har ingen idé om, hvor mange tegn brugeren skal indtaste 139 00:08:42,760 --> 00:08:46,590 før brugeren rent faktisk skriver dem. 140 00:08:46,590 --> 00:08:50,720 >> En naiv løsning på dette problem er at bare reservere en stor luns af plads, siger, 141 00:08:50,720 --> 00:08:54,540 en blok på 1000 tegn for brugerens indlæsning, 142 00:08:54,540 --> 00:08:57,980 forudsætning af, at brugeren aldrig ville skrive i en snor så længe. 143 00:08:57,980 --> 00:09:00,810 Det er en dårlig idé af to grunde. 144 00:09:00,810 --> 00:09:05,280 Først antager, at brugerne typisk ikke skrive strenge, der længe, 145 00:09:05,280 --> 00:09:07,610 du kunne spilde en masse hukommelse. 146 00:09:07,610 --> 00:09:10,530 På moderne maskiner, kan dette ikke være et problem hvis du gør dette 147 00:09:10,530 --> 00:09:13,890 i en eller to isolerede tilfælde, 148 00:09:13,890 --> 00:09:17,630 men hvis du tager brugerens input i en løkke og oplagring til senere brug, 149 00:09:17,630 --> 00:09:20,870 du kan hurtigt suge op et væld af hukommelse. 150 00:09:20,870 --> 00:09:24,450 Desuden, hvis det program, du skriver, er en mindre computer - 151 00:09:24,450 --> 00:09:28,100 en enhed som en smartphone eller noget andet med begrænset hukommelse - 152 00:09:28,100 --> 00:09:32,060 denne løsning vil skabe problemer meget hurtigere. 153 00:09:32,060 --> 00:09:36,450 Den anden, mere alvorlig grund til ikke at gøre dette er, at det efterlader dit program sårbar 154 00:09:36,450 --> 00:09:39,710 til, hvad der kaldes en buffer overflow angreb. 155 00:09:39,710 --> 00:09:45,840 I programmering, er en buffer hukommelse, der bruges til midlertidigt at gemme input eller output data, 156 00:09:45,840 --> 00:09:48,980 som i dette tilfælde er vores 1000-char blok. 157 00:09:48,980 --> 00:09:53,370 Et bufferoverløb opstår, når data skrives forbi enden af ​​blokken. 158 00:09:53,370 --> 00:09:57,790 >> For eksempel. Hvis en bruger rent faktisk type i mere end 1000 chars 159 00:09:57,790 --> 00:10:01,570 Du har måske oplevet dette ved et uheld, når du programmerer med arrays. 160 00:10:01,570 --> 00:10:05,620 Hvis du har en vifte af 10 int'er, intet stopper dig fra at forsøge at læse eller skrive 161 00:10:05,620 --> 00:10:07,810 den 15. int. 162 00:10:07,810 --> 00:10:10,000 Der er ingen compiler advarsler eller fejl. 163 00:10:10,000 --> 00:10:13,250 Programmet bare brølere ligeud og tilgår hukommelse 164 00:10:13,250 --> 00:10:18,150 hvor den mener den 15. int vil blive, og dette kan overskrive dine andre variabler. 165 00:10:18,150 --> 00:10:22,040 I værste fald kan du overskrive nogle af dit program interne 166 00:10:22,040 --> 00:10:26,820 kontrolmekanismer, at forårsage dit program faktisk at gennemføre forskellige instruktioner 167 00:10:26,820 --> 00:10:28,340 end du havde tænkt dig. 168 00:10:28,340 --> 00:10:31,360 >> Nu er det ikke almindeligt at gøre dette ved et uheld, 169 00:10:31,360 --> 00:10:35,150 men dette er en ret almindelig teknik, som skurkene bruge til at bryde programmer 170 00:10:35,150 --> 00:10:39,080 og sætte skadelig kode på andre folks computere. 171 00:10:39,080 --> 00:10:42,910 Derfor kan vi ikke bare bruge vores naive løsning. 172 00:10:42,910 --> 00:10:45,590 Vi har brug for en måde at forhindre vores programmer fra at være sårbar 173 00:10:45,590 --> 00:10:47,880 til en buffer overflow angreb. 174 00:10:47,880 --> 00:10:51,430 For at gøre dette, er vi nødt til at sikre, at vores buffer kan vokse, når vi læser 175 00:10:51,430 --> 00:10:53,850 more input fra brugeren. 176 00:10:53,850 --> 00:10:57,440 Løsningen? Vi bruger en bunke allokeret buffer. 177 00:10:57,440 --> 00:10:59,950 Da vi kan ændre størrelsen ved hjælp af resize den realloc funktion, 178 00:10:59,950 --> 00:11:04,580 og vi holde styr på to numre - indekset for den næste tomme slot i bufferen 179 00:11:04,580 --> 00:11:08,390 og længden eller kapacitet af bufferen. 180 00:11:08,390 --> 00:11:13,210 Vi læser i chars fra brugeren én ad gangen ved hjælp af fgetc funktionen. 181 00:11:13,210 --> 00:11:19,360 Argumentet den fgetc funktion tager - stdin - er en reference til den standard input strengen, 182 00:11:19,360 --> 00:11:23,810 som er en preconnected indgangskanal, der anvendes til at overføre brugerens indlæsning 183 00:11:23,810 --> 00:11:26,270 fra terminalen til programmet. 184 00:11:26,270 --> 00:11:29,890 >> Når brugeren skriver i en ny karakter, tjekker vi for at se, om indekset 185 00:11:29,890 --> 00:11:35,810 Den næste ledige plads plus 1 er større end kapaciteten af ​​pufferen. 186 00:11:35,810 --> 00:11:39,690 Da +1 kommer ind, fordi hvis det næste fri-indekset er 5, 187 00:11:39,690 --> 00:11:44,150 så vores buffer længde skal være 6 takket være 0 indeksering. 188 00:11:44,150 --> 00:11:48,350 Hvis vi har kørt ud af rummet i bufferen, så vi forsøger at ændre dens størrelse, 189 00:11:48,350 --> 00:11:51,690 fordobling så vi skære ned på det antal gange, som vi størrelsen 190 00:11:51,690 --> 00:11:54,760 hvis brugeren er at skrive i en virkelig lang snor. 191 00:11:54,760 --> 00:11:57,950 Hvis strengen har fået for lang, eller hvis vi løber tør for heap-hukommelse, 192 00:11:57,950 --> 00:12:01,350 vi befri vores buffer og tilbagevenden null. 193 00:12:01,350 --> 00:12:04,170 >> Endelig vil vi tilføje trækullet til bufferen. 194 00:12:04,170 --> 00:12:08,200 Når brugeren hits indtaste eller vende tilbage, signalerer en ny linje, 195 00:12:08,200 --> 00:12:12,050 eller den særlige char - kontrol d - som signalerer en ende af input, 196 00:12:12,050 --> 00:12:16,240 vi gør en check for at se, om brugeren rent faktisk har skrevet i noget som helst. 197 00:12:16,240 --> 00:12:18,820 Hvis ikke, vender vi null. 198 00:12:18,820 --> 00:12:22,280 Ellers fordi vores buffer er sandsynligvis større, end vi har brug for, 199 00:12:22,280 --> 00:12:24,830 i værste fald er det næsten dobbelt så stort som vi har brug for 200 00:12:24,830 --> 00:12:27,830 da vi fordoble hver gang vi ændrer størrelse, 201 00:12:27,830 --> 00:12:31,840 vi laver en ny kopi af strengen ved blot at bruge den mængde plads, som vi har brug for. 202 00:12:31,840 --> 00:12:34,220 Vi tilføjer et ekstra 1 til malloc opkaldet, 203 00:12:34,220 --> 00:12:37,810 så der er plads til den særlige null terminator karakter - \ 0, 204 00:12:37,810 --> 00:12:41,990 som vi tilføje til strengen, når vi kopierer i resten af ​​figurerne, 205 00:12:41,990 --> 00:12:45,060 ved hjælp strncpy stedet for strcpy 206 00:12:45,060 --> 00:12:48,830 så vi kan specificere præcis hvor mange tegn vi ønsker at kopiere. 207 00:12:48,830 --> 00:12:51,690 Strcpy kopier, indtil den rammer en \ 0. 208 00:12:51,690 --> 00:12:55,740 Så vi befri vores buffer og returnere kopien til den, der ringer. 209 00:12:55,740 --> 00:12:59,840 >> Hvem vidste sådan en simpel-tilsyneladende funktion kunne være så kompliceret? 210 00:12:59,840 --> 00:13:02,820 Nu ved du, hvad der går ind i CS50 biblioteket. 211 00:13:02,820 --> 00:13:06,470 >> Mit navn er Nate Hardison, og dette er CS50. 212 00:13:06,470 --> 00:13:08,350 [CS50.TV]