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 [Aquesta és CS50. CS50.TV] 4 00:00:07,260 --> 00:00:11,510 La biblioteca CS50 és una eina útil que ens hem instal · lat al aparell 5 00:00:11,510 --> 00:00:15,870 per fer més fàcil per a vostè per escriure programes que els usuaris sol · liciten dades. 6 00:00:15,870 --> 00:00:21,670 En aquest vídeo, anem a estirar la cortina i mira el que exactament està a la biblioteca CS50. 7 00:00:21,670 --> 00:00:25,520 >> En el vídeo en biblioteques C, parlem de com # include arxius de caps 8 00:00:25,520 --> 00:00:27,570 de la biblioteca en el codi font, 9 00:00:27,570 --> 00:00:31,150 i després connectar amb un arxiu de biblioteca binari durant la fase de vinculació 10 00:00:31,150 --> 00:00:33,140 del procés de compilació. 11 00:00:33,140 --> 00:00:36,440 Els arxius de capçalera especificar la interfície de la biblioteca. 12 00:00:36,440 --> 00:00:41,280 És a dir, que detalli tots els recursos que la biblioteca té disponible per al seu ús, 13 00:00:41,280 --> 00:00:45,250 com declaracions de funcions, constants i tipus de dades. 14 00:00:45,250 --> 00:00:48,890 L'arxiu de biblioteca binari conté l'aplicació de la biblioteca, 15 00:00:48,890 --> 00:00:54,580 que és una compilació dels arxius de capçalera de la biblioteca i les de la biblioteca. c fitxers de codi font. 16 00:00:54,580 --> 00:00:59,820 >> L'arxiu de biblioteca binària no és molt interessant de veure, ja que és, bé, en binari. 17 00:00:59,820 --> 00:01:03,300 Per tant, anem a fer una ullada als arxius de capçalera per a la biblioteca en el seu lloc. 18 00:01:03,300 --> 00:01:07,710 En aquest cas, només hi ha un arxiu de capçalera anomenat cs50.h. 19 00:01:07,710 --> 00:01:11,040 Ho hem instal · lat al directori d'usuari inclouen 20 00:01:11,040 --> 00:01:15,150 juntament amb els arxius de capçalera de les biblioteques d'altres sistemes. 21 00:01:15,150 --> 00:01:21,530 >> Una de les primeres coses que notarà és que cs50.h # inclou els arxius de capçalera d'altres biblioteques - 22 00:01:21,530 --> 00:01:25,670 float, bool límits, estàndard, estàndard i lib. 23 00:01:25,670 --> 00:01:28,800 Un cop més, seguint el principi de no reinventar la roda, 24 00:01:28,800 --> 00:01:33,490 hem construït la biblioteca CS0 l'ús d'eines que proporcionen altres per a nosaltres. 25 00:01:33,490 --> 00:01:38,690 >> El següent que veurem a la biblioteca és que es defineix un nou tipus anomenat "corda". 26 00:01:38,690 --> 00:01:42,330 Aquesta línia realment només crea un àlies per al tipus char *, 27 00:01:42,330 --> 00:01:46,000 pel que no torna imbuir al nou tipus de cadena amb atributs 28 00:01:46,000 --> 00:01:49,650 comunament associats amb objectes de cadena en altres idiomes, 29 00:01:49,650 --> 00:01:50,850 tals com la longitud. 30 00:01:50,850 --> 00:01:55,180 La raó per la qual fem això és per protegir els nous programadors dels detalls sagnants 31 00:01:55,180 --> 00:01:57,580 dels punters fins que estiguin llestos. 32 00:01:57,580 --> 00:02:00,130 >> La següent part de l'arxiu de capçalera és la declaració de les funcions 33 00:02:00,130 --> 00:02:04,410 que la biblioteca CS50 proporciona juntament amb la documentació. 34 00:02:04,410 --> 00:02:06,940 Observeu el nivell de detall en els comentaris aquí. 35 00:02:06,940 --> 00:02:10,560 Això és súper important perquè la gent sàpiga com utilitzar aquestes funcions. 36 00:02:10,560 --> 00:02:19,150 Declarem que, al seu torn, funciona per sol · licitar a l'usuari i caràcters de retorn, dobles, carrosses, sencers, 37 00:02:19,150 --> 00:02:24,160 llarg anhela, i les cordes, utilitzant el nostre propi tipus de cadena. 38 00:02:24,160 --> 00:02:26,260 Seguint el principi d'ocultació d'informació, 39 00:02:26,260 --> 00:02:31,640 hem posat la nostra definició en un arxiu d'execució separat c -. cs50.c-- 40 00:02:31,640 --> 00:02:35,110 ubicat al directori d'origen de l'usuari. 41 00:02:35,110 --> 00:02:38,040 Hem proporcionat aquest arxiu per tu per fer una ullada a ell, 42 00:02:38,040 --> 00:02:41,490 aprendre d'ella, i recompilar en màquines diferents si ho desitja, 43 00:02:41,490 --> 00:02:45,510 tot i que crec que és millor treballar en l'aparell d'aquesta classe. 44 00:02:45,510 --> 00:02:47,580 De tota manera, anem a fer una ullada ara. 45 00:02:49,020 --> 00:02:54,620 >> Les funcions getchar, GetDouble GetFloat, getInt, i GetLongLong 46 00:02:54,620 --> 00:02:58,160 es construeixen en la part superior de la funció GetString. 47 00:02:58,160 --> 00:03:01,510 Resulta que tots segueixen essencialment el mateix patró. 48 00:03:01,510 --> 00:03:04,870 Ells utilitzen un bucle while per sol · licitar a l'usuari una línia d'entrada. 49 00:03:04,870 --> 00:03:08,430 Ells tornen un valor especial si l'usuari introdueix una línia buida. 50 00:03:08,430 --> 00:03:11,750 S'intenta analitzar la entrada de l'usuari com el tipus adequat, 51 00:03:11,750 --> 00:03:15,010 ja sigui un char, un doble, un flotador, etc 52 00:03:15,010 --> 00:03:18,710 I llavors o bé tornar el resultat si l'entrada s'ha analitzat correctament 53 00:03:18,710 --> 00:03:21,330 o reprompt l'usuari. 54 00:03:21,330 --> 00:03:24,230 >> A un alt nivell, no hi ha res realment difícil aquí. 55 00:03:24,230 --> 00:03:28,760 És possible que hagi escrit el codi d'estructura similar a tu mateix en el passat. 56 00:03:28,760 --> 00:03:34,720 Potser la part més secreta d'aspecte és l'anomenada sscanf que analitza l'entrada de l'usuari. 57 00:03:34,720 --> 00:03:38,160 Sscanf és part de la família d'entrada de conversió de format. 58 00:03:38,160 --> 00:03:42,300 Viu en io.h estàndard, i el seu treball consisteix a analitzar una cadena C, 59 00:03:42,300 --> 00:03:46,520 segons un format determinat, l'emmagatzematge dels resultats d'anàlisi en variables 60 00:03:46,520 --> 00:03:48,720 proporcionat pel picador. 61 00:03:48,720 --> 00:03:53,570 Atès que les funcions de conversió de format d'entrada són molt útils, les funcions utilitzades 62 00:03:53,570 --> 00:03:56,160 que no són súper intuïtiu al principi, 63 00:03:56,160 --> 00:03:58,300 anem a repassar com funciona sscanf. 64 00:03:58,300 --> 00:04:03,330 >> El primer argument d'sscanf és un char * - un punter a un caràcter. 65 00:04:03,330 --> 00:04:05,150 Perquè la funció funcioni correctament, 66 00:04:05,150 --> 00:04:08,340 que el caràcter ha de ser el primer caràcter d'una cadena C, 67 00:04:08,340 --> 00:04:12,270 acabar amb el nul caràcter \ 0. 68 00:04:12,270 --> 00:04:15,120 Aquesta és la cadena per analitzar 69 00:04:15,120 --> 00:04:18,269 El segon argument d'sscanf és una cadena de format, 70 00:04:18,269 --> 00:04:20,839 normalment passa com una cadena constant, 71 00:04:20,839 --> 00:04:24,040 i que podria haver vist una cadena com aquesta abans quan s'utilitza printf. 72 00:04:24,040 --> 00:04:28,650 Un signe de percentatge en la cadena de format indica un especificador de conversió. 73 00:04:28,650 --> 00:04:30,850 El caràcter immediatament després d'un signe de percentatge, 74 00:04:30,850 --> 00:04:35,430 indica el tipus C que volem sscanf convertir. 75 00:04:35,430 --> 00:04:40,090 En getInt, es veu que hi ha un d% i un% c. 76 00:04:40,090 --> 00:04:48,690 Això vol dir que sscanf tractarà d'un decimal int - el% d - i aprofitar a - c%. 77 00:04:48,690 --> 00:04:51,510 Per a cada indicador de conversió en la cadena de format, 78 00:04:51,510 --> 00:04:56,620 sscanf espera un argument corresponent més endavant en la llista de paràmetres. 79 00:04:56,620 --> 00:05:00,850 Aquest argument ha d'apuntar a una ubicació apropiada amb tipus 80 00:05:00,850 --> 00:05:04,000 en què emmagatzemar el resultat de la conversió. 81 00:05:04,000 --> 00:05:08,910 >> La forma típica de fer això és crear una variable a la pila abans de l'anomenada sscanf 82 00:05:08,910 --> 00:05:11,440 per a cada element que voleu analitzar de la cadena 83 00:05:11,440 --> 00:05:15,520 a continuació, utilitzeu l'operador de direcció - el signe - per passar punters 84 00:05:15,520 --> 00:05:19,100 a les variables de l'anomenada sscanf. 85 00:05:19,100 --> 00:05:22,720 Es pot veure que en getInt de fer exactament això. 86 00:05:22,720 --> 00:05:28,240 Just abans de l'anomenada sscanf, declarem un enter anomenat n i aire anomenada carbó a la pila, 87 00:05:28,240 --> 00:05:32,340 i passem punters a ells en l'anomenada sscanf. 88 00:05:32,340 --> 00:05:35,800 Posant aquestes variables en la pila és preferible a utilitzar l'espai assignat 89 00:05:35,800 --> 00:05:39,350 al munt amb malloc, ja que s'evita la sobrecàrrega de l'anomenada malloc, 90 00:05:39,350 --> 00:05:43,060 i vostè no ha de preocupar-se per fuites de memòria. 91 00:05:43,060 --> 00:05:47,280 Els personatges no precedits per un signe de percentatge no sol · liciten la conversió. 92 00:05:47,280 --> 00:05:50,380 Més aviat, només ha d'afegir a l'especificació de format. 93 00:05:50,380 --> 00:05:56,500 >> Per exemple, si la cadena de format en getint eren un d% en lloc, 94 00:05:56,500 --> 00:05:59,800 sscanf buscaria la lletra A seguida d'un int, 95 00:05:59,800 --> 00:06:04,360 i si bé s'intentarà convertir el int, no fer res més amb el a. 96 00:06:04,360 --> 00:06:07,440 L'única excepció a això és l'espai en blanc. 97 00:06:07,440 --> 00:06:11,030 Els espais en blanc en la cadena de format coincideix amb cap quantitat d'espai en blanc - 98 00:06:11,030 --> 00:06:12,890 fins i tot cap en absolut. 99 00:06:12,890 --> 00:06:18,100 Així que, per això el comentari esmenta possiblement amb la direcció i / o espais en blanc. 100 00:06:18,100 --> 00:06:22,910 Per tant, en aquest moment sembla que la nostra crida sscanf tractarà d'analitzar la cadena d'entrada de l'usuari 101 00:06:22,910 --> 00:06:25,380 mitjançant la comprovació de possibles espais en blanc, 102 00:06:25,380 --> 00:06:29,300 seguit d'un int que es converteix i s'emmagatzema en la variable int n 103 00:06:29,300 --> 00:06:33,090 seguit per una certa quantitat d'espai en blanc, i seguit per un caràcter 104 00:06:33,090 --> 00:06:35,810 emmagatzemat en la variable c char. 105 00:06:35,810 --> 00:06:37,790 >> Què passa amb el valor de retorn? 106 00:06:37,790 --> 00:06:41,560 Sscanf analitzarà la línia d'entrada de principi a fi, 107 00:06:41,560 --> 00:06:44,860 s'atura quan arriba al final o quan un personatge a l'entrada 108 00:06:44,860 --> 00:06:49,320 no coincideix amb un caràcter de format o quan no es pot fer una conversió. 109 00:06:49,320 --> 00:06:52,690 El seu valor de retorn s'utilitza per distingir quan es va aturar. 110 00:06:52,690 --> 00:06:55,670 Si es va aturar, perquè ha arribat al final de la cadena d'entrada 111 00:06:55,670 --> 00:07:00,630 abans de realitzar les conversions i abans de fallar perquè coincideixi amb part de la cadena de format, 112 00:07:00,630 --> 00:07:04,840 llavors la constant EOF especial és retornat. 113 00:07:04,840 --> 00:07:08,200 En cas contrari, retorna el nombre de conversions reeixides, 114 00:07:08,200 --> 00:07:14,380 que pot ser 0, 1 o 2, ja que hem demanat dues conversions. 115 00:07:14,380 --> 00:07:19,000 En el nostre cas, volem assegurar-nos que l'usuari va escriure en un enter i només un int. 116 00:07:19,000 --> 00:07:23,370 >> Per tant, volem tornar a sscanf 1. Vegi per què? 117 00:07:23,370 --> 00:07:26,850 Si sscanf trobat 0, llavors no hi ha conversions es van fer, 118 00:07:26,850 --> 00:07:31,690 de manera que l'usuari ha escrit alguna cosa diferent d'un int al començament de l'entrada. 119 00:07:31,690 --> 00:07:37,100 Si sscanf retorna 2, llavors l'usuari va tenir degudament escriviu en el començament de l'entrada, 120 00:07:37,100 --> 00:07:41,390 però després es passen en algun caràcter que no sigui espai en blanc després 121 00:07:41,390 --> 00:07:44,940 ja que el% c conversió va tenir èxit. 122 00:07:44,940 --> 00:07:49,570 Wow, això és bastant llarga explicació per una crida a la funció. 123 00:07:49,570 --> 00:07:53,460 De tota manera, si vols més informació sobre sscanf i els seus germans, 124 00:07:53,460 --> 00:07:57,130 fes un cop d'ull a les pàgines de manual, de Google, o tots dos. 125 00:07:57,130 --> 00:07:58,780 Hi ha un munt d'opcions de format de cadena, 126 00:07:58,780 --> 00:08:03,830 i aquests poden estalviar una gran quantitat de mà d'obra quan es tracta d'analitzar cadenes en C. 127 00:08:03,830 --> 00:08:07,180 >> L'última funció a la biblioteca per tenir en compte és GetString. 128 00:08:07,180 --> 00:08:10,310 Resulta que GetString és una funció difícil d'escriure correctament, 129 00:08:10,310 --> 00:08:14,290 tot i que sembla una tasca senzilla, comú. 130 00:08:14,290 --> 00:08:16,170 Per què és aquest el cas? 131 00:08:16,170 --> 00:08:21,380 Bé, anem a pensar en com anem a guardar la línia que l'usuari escriu polz 132 00:08:21,380 --> 00:08:23,880 Atès que una cadena és una seqüència de caràcters, 133 00:08:23,880 --> 00:08:26,430 el que es vol emmagatzemar en una matriu en la pila, 134 00:08:26,430 --> 00:08:31,250 però necessitaríem saber quant temps la matriu serà quan ho declari. 135 00:08:31,250 --> 00:08:34,030 De la mateixa manera, si volem posar-lo al munt, 136 00:08:34,030 --> 00:08:38,090 hem de passar a malloc el nombre de bytes que desitja reservar, 137 00:08:38,090 --> 00:08:39,730 però això és impossible. 138 00:08:39,730 --> 00:08:42,760 No tenim idea de com molts caràcters que l'usuari escrigui en 139 00:08:42,760 --> 00:08:46,590 abans que l'usuari realment es els escriu. 140 00:08:46,590 --> 00:08:50,720 >> Una solució ingènua a aquest problema és simplement per reservar una gran part de l'espai, per exemple, 141 00:08:50,720 --> 00:08:54,540 un bloc de 1000 caràcters per a l'entrada de l'usuari, 142 00:08:54,540 --> 00:08:57,980 suposant que l'usuari mai hauria escriure en una cadena llarga. 143 00:08:57,980 --> 00:09:00,810 Aquesta és una mala idea per dues raons. 144 00:09:00,810 --> 00:09:05,280 En primer lloc, en el cas que els usuaris no solen escriure en les cadenes de tant de temps, 145 00:09:05,280 --> 00:09:07,610 vostè podria perdre una gran quantitat de memòria. 146 00:09:07,610 --> 00:09:10,530 En les màquines modernes, això podria no ser un problema si vostè fa això 147 00:09:10,530 --> 00:09:13,890 en un o dos casos aïllats, 148 00:09:13,890 --> 00:09:17,630 però si vostè està prenent l'entrada de l'usuari en un bucle i emmagatzemar per al seu ús posterior, 149 00:09:17,630 --> 00:09:20,870 vostè pot absorbir una tona de memòria. 150 00:09:20,870 --> 00:09:24,450 A més, si el programa que s'està escrivint és per a un equip més petit - 151 00:09:24,450 --> 00:09:28,100 un dispositiu com un telèfon intel · ligent o alguna cosa més amb memòria limitada - 152 00:09:28,100 --> 00:09:32,060 aquesta solució va a causar problemes molt més ràpid. 153 00:09:32,060 --> 00:09:36,450 El segon motiu més greu per no fer-ho és que deixa el seu programa vulnerable 154 00:09:36,450 --> 00:09:39,710 al que es diu un atac de desbordament de memòria intermèdia. 155 00:09:39,710 --> 00:09:45,840 En programació, un buffer és la memòria utilitzada per emmagatzemar temporalment les dades d'entrada o sortida, 156 00:09:45,840 --> 00:09:48,980 que en aquest cas és el nostre 1000-carbó bloc. 157 00:09:48,980 --> 00:09:53,370 Un desbordament de memòria es produeix quan les dades s'escriuen més enllà del final del bloc. 158 00:09:53,370 --> 00:09:57,790 >> Per exemple, si un usuari fa realment tipus en més de 1000 caràcters. 159 00:09:57,790 --> 00:10:01,570 És possible que hagi tingut aquest accident quan es programa amb matrius. 160 00:10:01,570 --> 00:10:05,620 Si vostè té un arranjament de 10 punts, res no impedeix intentar llegir o escriure 161 00:10:05,620 --> 00:10:07,810 el int 15. 162 00:10:07,810 --> 00:10:10,000 No hi ha advertències o errors del compilador. 163 00:10:10,000 --> 00:10:13,250 El programa simplement errors de front i té accés a la memòria 164 00:10:13,250 --> 00:10:18,150 on es creu que el int 15 serà, i això pot sobreescriure altres variables. 165 00:10:18,150 --> 00:10:22,040 En el pitjor dels casos, pot sobreescriure alguns dels interns del seu programa 166 00:10:22,040 --> 00:10:26,820 mecanismes de control, la causa del seu programa a executar realment diferents instruccions 167 00:10:26,820 --> 00:10:28,340 que vostè pretén. 168 00:10:28,340 --> 00:10:31,360 >> Ara, no és comú fer això per accident, 169 00:10:31,360 --> 00:10:35,150 però aquesta és una tècnica bastant comú que els mals usen per trencar programes 170 00:10:35,150 --> 00:10:39,080 i posar el codi maliciós en els ordinadors d'altres persones. 171 00:10:39,080 --> 00:10:42,910 Per tant, nosaltres no podem usar la nostra solució ingènua. 172 00:10:42,910 --> 00:10:45,590 Necessitem una forma d'evitar que els nostres programes de ser vulnerable 173 00:10:45,590 --> 00:10:47,880 a un atac de desbordament de memòria intermèdia. 174 00:10:47,880 --> 00:10:51,430 Per això, hem d'assegurar que el nostre buffer pot créixer a mesura que llegim 175 00:10:51,430 --> 00:10:53,850 més informació de l'usuari. 176 00:10:53,850 --> 00:10:57,440 La solució? Fem servir un munt memòria intermèdia assignat. 177 00:10:57,440 --> 00:10:59,950 Com que podem canviar la seva mida utilitzant el canvi de mida de la funció realloc, 178 00:10:59,950 --> 00:11:04,580 i fem un seguiment de dos nombres - l'índex de la ranura buida següent en el buffer 179 00:11:04,580 --> 00:11:08,390 i la longitud o la capacitat de la memòria intermèdia. 180 00:11:08,390 --> 00:11:13,210 Llegim en caràcters des de l'usuari alhora utilitzant la funció fgetc. 181 00:11:13,210 --> 00:11:19,360 L'argument de la funció de presa fgetc - stdin - és una referència a la cadena d'entrada estàndard, 182 00:11:19,360 --> 00:11:23,810 que és un canal d'entrada preconectado que s'utilitza per transferir la entrada de l'usuari 183 00:11:23,810 --> 00:11:26,270 des del terminal al programa. 184 00:11:26,270 --> 00:11:29,890 >> Cada vegada que l'usuari escriu en un nou personatge, comprovem si l'índex 185 00:11:29,890 --> 00:11:35,810 de la ranura lliure següent més 1 és més gran que la capacitat de la memòria intermèdia. 186 00:11:35,810 --> 00:11:39,690 L'un entra perquè si l'índex gratis al costat és de 5, 187 00:11:39,690 --> 00:11:44,150 doncs el llarg nostre buffer ha de ser de 6 a 0 gràcies indexació. 188 00:11:44,150 --> 00:11:48,350 Si ens hem quedat sense espai en el buffer, llavors intentem canviar la seva mida, 189 00:11:48,350 --> 00:11:51,690 lo de manera que reduir el nombre de vegades que es canvia la mida 190 00:11:51,690 --> 00:11:54,760 si l'usuari està escrivint en una cadena molt llarga. 191 00:11:54,760 --> 00:11:57,950 Si la cadena s'ha fet massa llarg o si es queda sense memòria heap, 192 00:11:57,950 --> 00:12:01,350 alliberem el nostre buffer i nul retorn. 193 00:12:01,350 --> 00:12:04,170 >> Finalment, afegim el char a la memòria intermèdia. 194 00:12:04,170 --> 00:12:08,200 Quan els accessos d'usuari ingressar o retornar, assenyalant una nova línia, 195 00:12:08,200 --> 00:12:12,050 o l'especial de caràcters - Control d - el que indica un final de l'entrada, 196 00:12:12,050 --> 00:12:16,240 fem una comprovació per veure si l'usuari realment escriure en res en absolut. 197 00:12:16,240 --> 00:12:18,820 Si no, retorna null. 198 00:12:18,820 --> 00:12:22,280 En cas contrari, ja que el nostre buffer és probablement més gran del que necessitem, 199 00:12:22,280 --> 00:12:24,830 en el pitjor dels casos és gairebé dues vegades tan gran com cal 200 00:12:24,830 --> 00:12:27,830 ja que es duplica cada vegada que canvia la mida, 201 00:12:27,830 --> 00:12:31,840 es fa una nova còpia de la cadena amb la quantitat d'espai que necessitem. 202 00:12:31,840 --> 00:12:34,220 Afegim un extra d'1 a l'anomenada malloc, 203 00:12:34,220 --> 00:12:37,810 de manera que hi ha espai per l'especial caràcter nul terminador - el \ 0, 204 00:12:37,810 --> 00:12:41,990 afegir a la cadena una vegada de copiar a la resta dels caràcters, 205 00:12:41,990 --> 00:12:45,060 utilitzant strncpy en lloc de strcpy 206 00:12:45,060 --> 00:12:48,830 de manera que podem especificar exactament com molts caràcters que voleu copiar. 207 00:12:48,830 --> 00:12:51,690 Strcpy còpia fins que arriba a \ 0. 208 00:12:51,690 --> 00:12:55,740 Llavors alliberem el nostre buffer i tornar la còpia a la persona que truca. 209 00:12:55,740 --> 00:12:59,840 >> Qui sabia que aquesta funció aparentment simple podria ser tan complicat? 210 00:12:59,840 --> 00:13:02,820 Ara vostè sap el que entra a la biblioteca CS50. 211 00:13:02,820 --> 00:13:06,470 >> El meu nom és Nate Hardison, i això és CS50. 212 00:13:06,470 --> 00:13:08,350 [CS50.TV]