1 00:00:00,000 --> 00:00:02,490 [Powered by Google Translate] [CS50 Biblioteca] 2 00:00:02,490 --> 00:00:04,220 [Nate Hardison] [Harvard University] 3 00:00:04,220 --> 00:00:07,260 [Esta é CS50. CS50.TV] 4 00:00:07,260 --> 00:00:11,510 A biblioteca CS50 é unha ferramenta útil que temos instalado no aparello 5 00:00:11,510 --> 00:00:15,870 para facelo máis doado para ti escribir programas que solicitan entrada dos usuarios. 6 00:00:15,870 --> 00:00:21,670 Neste vídeo, imos tirar a cortina e ollar para o que é exactamente na biblioteca CS50. 7 00:00:21,670 --> 00:00:25,520 >> No vídeo en bibliotecas C, falamos sobre como # include arquivos cabeceiras 8 00:00:25,520 --> 00:00:27,570 da biblioteca no seu código fonte, 9 00:00:27,570 --> 00:00:31,150 e entón conectar cun arquivo de biblioteca binario durante a fase de vinculación 10 00:00:31,150 --> 00:00:33,140 do proceso de compilación. 11 00:00:33,140 --> 00:00:36,440 Os ficheiros de cabeceira especificar a interface da biblioteca. 12 00:00:36,440 --> 00:00:41,280 É dicir, eles detalladamente todos os recursos que a biblioteca ten dispoñible para usar, 13 00:00:41,280 --> 00:00:45,250 como declaracións de funcións, constantes e tipos de datos. 14 00:00:45,250 --> 00:00:48,890 O arquivo de biblioteca binario contén a implementación da biblioteca, 15 00:00:48,890 --> 00:00:54,580 que está feita a partir de arquivos da biblioteca de cabeceira e as da biblioteca. c arquivos de código fonte. 16 00:00:54,580 --> 00:00:59,820 >> O arquivo de biblioteca binario non é moi interesante para ollar xa que é, así, en binario. 17 00:00:59,820 --> 00:01:03,300 Entón, imos dar un ollo nos arquivos de cabeceira para a biblioteca vez. 18 00:01:03,300 --> 00:01:07,710 Neste caso, hai só un arquivo de cabeceira chamado cs50.h. 19 00:01:07,710 --> 00:01:11,040 Nós instalado no usuario incluír o directorio 20 00:01:11,040 --> 00:01:15,150 xunto cos arquivos das bibliotecas do sistema de outros cabeceira. 21 00:01:15,150 --> 00:01:21,530 >> Unha das primeiras cousas que vai notar é que cs50.h # inclúe ficheiros de cabeceira doutras bibliotecas - 22 00:01:21,530 --> 00:01:25,670 float, límites, patrón bool e lib estándar. 23 00:01:25,670 --> 00:01:28,800 Unha vez máis, seguindo o principio de non reinventar a roda, 24 00:01:28,800 --> 00:01:33,490 Nós construímos a biblioteca CS0 empregando as ferramentas que outros subministrados por nós. 25 00:01:33,490 --> 00:01:38,690 >> A seguinte cousa que vai ver na biblioteca é que definimos un novo tipo chamado "cadea". 26 00:01:38,690 --> 00:01:42,330 Esta liña realmente só crea un alias para o tipo char *, 27 00:01:42,330 --> 00:01:46,000 para que non pase de máxica Inculcar o novo tipo de grupo con atributos 28 00:01:46,000 --> 00:01:49,650 comunmente asociado con obxectos de cadea noutras linguas, 29 00:01:49,650 --> 00:01:50,850 como a lonxitude. 30 00:01:50,850 --> 00:01:55,180 A razón por que fixen isto é para protexer novos desenvolvedores de detalles 31 00:01:55,180 --> 00:01:57,580 de punteiros ata que estean listos. 32 00:01:57,580 --> 00:02:00,130 >> A seguinte parte do arquivo de cabeceira é a declaración das funcións 33 00:02:00,130 --> 00:02:04,410 que a biblioteca CS50 proporciona, xunto coa documentación. 34 00:02:04,410 --> 00:02:06,940 Teña en conta o nivel de detalle nos comentarios aquí. 35 00:02:06,940 --> 00:02:10,560 Isto é super importante para que as persoas saben como usar estas funcións. 36 00:02:10,560 --> 00:02:19,150 Nós declaramos, á súa vez, traballa para solicitar ao usuario e caracteres de retorno, dobres, coches alegóricos, ints, 37 00:02:19,150 --> 00:02:24,160 tempo ansia, e cordas, usando o noso propio tipo cadea. 38 00:02:24,160 --> 00:02:26,260 Seguindo o principio de ocultación de información, 39 00:02:26,260 --> 00:02:31,640 temos que poñer a nosa definición nun ficheiro de implementación separada C -. cs50.c - 40 00:02:31,640 --> 00:02:35,110 situado no directorio de orixe do autor. 41 00:02:35,110 --> 00:02:38,040 Nós fornecen o ficheiro de modo que pode dar un ollo niso, 42 00:02:38,040 --> 00:02:41,490 aprender con el, e recompilar en máquinas diferentes, se o desexa, 43 00:02:41,490 --> 00:02:45,510 aínda que nós cremos que é mellor para traballar no dispositivo para esta clase. 44 00:02:45,510 --> 00:02:47,580 En calquera caso, imos dar un ollo nel agora. 45 00:02:49,020 --> 00:02:54,620 >> As funcións getchar, GetDouble, GetFloat, GetInt e GetLongLong 46 00:02:54,620 --> 00:02:58,160 son todos construídos na parte superior da función GetString. 47 00:02:58,160 --> 00:03:01,510 Acontece que todos eles seguen basicamente o mesmo patrón. 48 00:03:01,510 --> 00:03:04,870 Eles usan un loop while para solicitar ao usuario unha liña de entrada. 49 00:03:04,870 --> 00:03:08,430 Eles retornan un valor especial se o usuario inserir unha liña baleira. 50 00:03:08,430 --> 00:03:11,750 Eles tratan de analizar a entrada do usuario como o tipo apropiado, 51 00:03:11,750 --> 00:03:15,010 sexa un char, un dúo, un float, etc 52 00:03:15,010 --> 00:03:18,710 E entón eles ou devolver o resultado a entrada foi analizado correctamente 53 00:03:18,710 --> 00:03:21,330 ou eles Reprompt o usuario. 54 00:03:21,330 --> 00:03:24,230 >> Nun nivel alto, non hai nada moi complicado aquí. 55 00:03:24,230 --> 00:03:28,760 Podes escribir o código de estrutura semellante a si mesmo no pasado. 56 00:03:28,760 --> 00:03:34,720 Quizais a parte máis enigmática para o futuro é a chamada sscanf que analiza a entrada do usuario. 57 00:03:34,720 --> 00:03:38,160 Sscanf é parte da familia de conversión de entrada formato. 58 00:03:38,160 --> 00:03:42,300 Vive en io.h estándar, eo seu traballo é analizar unha secuencia C, 59 00:03:42,300 --> 00:03:46,520 de acordo cun formato específico, almacenando os resultados da análise da variable 60 00:03:46,520 --> 00:03:48,720 fornecido polo chamador. 61 00:03:48,720 --> 00:03:53,570 Dende a entrada de funcións de conversión de formato son moi útiles, as funcións amplamente utilizados 62 00:03:53,570 --> 00:03:56,160 que non son super intuitivo en primeiro lugar, 63 00:03:56,160 --> 00:03:58,300 nós falaremos sobre como sscanf funciona. 64 00:03:58,300 --> 00:04:03,330 >> O primeiro argumento para sscanf é un char * - un punteiro para un personaxe. 65 00:04:03,330 --> 00:04:05,150 Para a función de traballar correctamente, 66 00:04:05,150 --> 00:04:08,340 ese carácter debe ser o primeiro carácter dunha cadea C, 67 00:04:08,340 --> 00:04:12,270 rematou o nulo \ 0 personaxe. 68 00:04:12,270 --> 00:04:15,120 Esta é a cadea para analizar 69 00:04:15,120 --> 00:04:18,269 O segundo argumento para sscanf é unha secuencia de formato, 70 00:04:18,269 --> 00:04:20,839 tipicamente pasado como unha constante cadea, 71 00:04:20,839 --> 00:04:24,040 e pode ver unha secuencia como esta antes, cando usar printf. 72 00:04:24,040 --> 00:04:28,650 Un sinal de porcentaxe no formato cadea indica un especificador de conversión. 73 00:04:28,650 --> 00:04:30,850 O carácter inmediatamente despois un sinal de porcentaxe, 74 00:04:30,850 --> 00:04:35,430 indica o tipo C que queremos sscanf para converter. 75 00:04:35,430 --> 00:04:40,090 En GetInt, ve que hai unha d% e% c. 76 00:04:40,090 --> 00:04:48,690 Isto significa que sscanf vai un int decimal - o% d - e un char - o c%. 77 00:04:48,690 --> 00:04:51,510 Para cada especificador de conversión na cadea de formato, 78 00:04:51,510 --> 00:04:56,620 sscanf espera un argumento correspondente máis tarde, na súa lista de argumentos. 79 00:04:56,620 --> 00:05:00,850 Ese argumento debe apuntar a un lugar debidamente ingresaran 80 00:05:00,850 --> 00:05:04,000 en que para almacenar o resultado da conversión. 81 00:05:04,000 --> 00:05:08,910 >> A forma típica de facelo é crear unha variable na pila antes da chamada sscanf 82 00:05:08,910 --> 00:05:11,440 para cada elemento que quere analizar a partir da cadea 83 00:05:11,440 --> 00:05:15,520 e entón usar o operador de enderezo - o comercial - para pasar punteiros 84 00:05:15,520 --> 00:05:19,100 a esas variables para a chamada sscanf. 85 00:05:19,100 --> 00:05:22,720 Podes ver que en GetInt facemos exactamente isto. 86 00:05:22,720 --> 00:05:28,240 Logo antes de chamada sscanf, nós declaramos un int chamado n e un c chamada char na pila, 87 00:05:28,240 --> 00:05:32,340 e pasamos punteiros para eles na chamada sscanf. 88 00:05:32,340 --> 00:05:35,800 Poñer esas variables na pila é preferible usar o espazo alocado 89 00:05:35,800 --> 00:05:39,350 no heap con malloc, unha vez que evitar a sobrecarga da chamada malloc, 90 00:05:39,350 --> 00:05:43,060 e non ten que se preocupar con baleirado de memoria. 91 00:05:43,060 --> 00:05:47,280 Caracteres non prefixadas por un sinal de porcentaxe non solicitan a conversión. 92 00:05:47,280 --> 00:05:50,380 En vez diso, pode engadir a especificación de formato. 93 00:05:50,380 --> 00:05:56,500 >> Por exemplo, se a secuencia de formato en GetInt fose un d% en vez diso, 94 00:05:56,500 --> 00:05:59,800 sscanf ía buscar a letra seguido dun int, 95 00:05:59,800 --> 00:06:04,360 e mentres el tentaría converter o int, que non ía facer nada co un. 96 00:06:04,360 --> 00:06:07,440 A única excepción a iso é espazo en branco. 97 00:06:07,440 --> 00:06:11,030 Caracteres de espazo en branco na cadea formato de combinar con calquera cantidade de espazos en branco - 98 00:06:11,030 --> 00:06:12,890 mesmo ningún. 99 00:06:12,890 --> 00:06:18,100 Entón, é por iso que o comentario menciona posiblemente cos principais e / ou espazos en branco. 100 00:06:18,100 --> 00:06:22,910 Entón, neste momento, parece que o noso chamamento sscanf intentará analizar a secuencia de entrada do usuario 101 00:06:22,910 --> 00:06:25,380 comprobando posibles espazos en branco, 102 00:06:25,380 --> 00:06:29,300 seguido dun int que ha ser convertida e almacenada na variable int n 103 00:06:29,300 --> 00:06:33,090 seguido por unha certa cantidade de espazos en branco, e seguido por un carácter 104 00:06:33,090 --> 00:06:35,810 almacenado na variable char c. 105 00:06:35,810 --> 00:06:37,790 >> E sobre o valor de retorno? 106 00:06:37,790 --> 00:06:41,560 Sscanf ha analizar a liña de entrada do principio ao final, 107 00:06:41,560 --> 00:06:44,860 parando cando chega o final ou cando un carácter no input 108 00:06:44,860 --> 00:06:49,320 non corresponde a un personaxe formato ou cando non pode facer unha conversión. 109 00:06:49,320 --> 00:06:52,690 O seu valor de retorno é usado para illar cando parou. 110 00:06:52,690 --> 00:06:55,670 Se parou, porque chegou ao fin da cadea de entrada 111 00:06:55,670 --> 00:07:00,630 antes de facer as conversións e antes de deixar de coincidir con parte da cadea de formato, 112 00:07:00,630 --> 00:07:04,840 a continuación, o EOF especial constante é retornado. 113 00:07:04,840 --> 00:07:08,200 Se non, el retorna o número de conversións exitosa, 114 00:07:08,200 --> 00:07:14,380 o que pode ser 0, 1 ou 2, unha vez que xa pediu dúas conversións. 115 00:07:14,380 --> 00:07:19,000 No noso caso, queremos estar seguro de que o usuario introduciu un int e só un int. 116 00:07:19,000 --> 00:07:23,370 >> Entón, nós queremos sscanf para voltar 1. Vexa por que? 117 00:07:23,370 --> 00:07:26,850 Se sscanf retornado 0, entón non conversións foron feitas, 118 00:07:26,850 --> 00:07:31,690 de xeito que o usuario escribiu algo diferente dun int no inicio da entrada. 119 00:07:31,690 --> 00:07:37,100 Se sscanf retorna 2, entón o usuario non escribe correctamente no comezo da entrada, 120 00:07:37,100 --> 00:07:41,390 pero entón escritas nalgúns caracteres non espazo en branco despois 121 00:07:41,390 --> 00:07:44,940 unha vez que a% c conversión tivo éxito. 122 00:07:44,940 --> 00:07:49,570 Guau, iso é unha longa explicación para unha chamada de función. 123 00:07:49,570 --> 00:07:53,460 En fin, se queres máis información sobre sscanf e os seus irmáns, 124 00:07:53,460 --> 00:07:57,130 Consulte as páxinas de manual, Google, ou ambos. 125 00:07:57,130 --> 00:07:58,780 Hai moitas opcións de cadea de formato, 126 00:07:58,780 --> 00:08:03,830 e estes poden aforrar-lle moito do traballo manual ao intentar analizar secuencias en C. 127 00:08:03,830 --> 00:08:07,180 >> A última función na biblioteca é mirar para GetString. 128 00:08:07,180 --> 00:08:10,310 Acontece que GetString é unha función difícil de escribir correctamente, 129 00:08:10,310 --> 00:08:14,290 aínda que parece que tal unha tarefa sinxela, común. 130 00:08:14,290 --> 00:08:16,170 Por que é este o caso? 131 00:08:16,170 --> 00:08:21,380 Ben, imos pensar sobre como estamos indo para almacenar a liña que o usuario escribe Pol 132 00:08:21,380 --> 00:08:23,880 Unha vez que unha cadea é unha secuencia de caracteres, 133 00:08:23,880 --> 00:08:26,430 podemos querer almacena-lo nunha matriz na pila, 134 00:08:26,430 --> 00:08:31,250 pero sería preciso saber canto tempo a matriz vai ser cando declara. 135 00:08:31,250 --> 00:08:34,030 Do mesmo xeito, se queremos poñelas na pila, 136 00:08:34,030 --> 00:08:38,090 necesitamos pasar malloc o número de bytes que queremos reserva, 137 00:08:38,090 --> 00:08:39,730 pero isto é imposible. 138 00:08:39,730 --> 00:08:42,760 Nós non temos idea de cantos caracteres que o usuario escriba 139 00:08:42,760 --> 00:08:46,590 antes de que o usuario non escribe. 140 00:08:46,590 --> 00:08:50,720 >> Unha solución inxenua para este problema é só reservar un anaco grande de espazo, por exemplo, 141 00:08:50,720 --> 00:08:54,540 un bloque de 1000 caracteres para a entrada do usuario, 142 00:08:54,540 --> 00:08:57,980 asumindo que o usuario nunca debería escribir unha secuencia de tanto tempo. 143 00:08:57,980 --> 00:09:00,810 Esta é unha idea malo por dous motivos. 144 00:09:00,810 --> 00:09:05,280 En primeiro lugar, asumindo que os usuarios normalmente non escribir en cordas moito tempo, 145 00:09:05,280 --> 00:09:07,610 podería perder unha grande cantidade de memoria. 146 00:09:07,610 --> 00:09:10,530 En máquinas modernas, isto pode non ser un problema se fai iso 147 00:09:10,530 --> 00:09:13,890 en un ou dous casos illados, 148 00:09:13,890 --> 00:09:17,630 pero se está tomando a entrada do usuario en un loop e almacenamento para o seu uso posterior, 149 00:09:17,630 --> 00:09:20,870 pode rapidamente Sugar ata unha tonelada de memoria. 150 00:09:20,870 --> 00:09:24,450 Ademais, o programa que está escribindo é un ordenador pequeno - 151 00:09:24,450 --> 00:09:28,100 un dispositivo como un teléfono ou calquera outra cousa con memoria limitada - 152 00:09:28,100 --> 00:09:32,060 esta solución pode causar problemas moito máis rápido. 153 00:09:32,060 --> 00:09:36,450 O motivo, segundo máis grave non facer iso é que el deixa o seu programa vulnerable 154 00:09:36,450 --> 00:09:39,710 para o que se denomina un ataque de estourido de buffer. 155 00:09:39,710 --> 00:09:45,840 Na programación, un buffer de memoria é usado para almacenar temporalmente os datos de entrada ou de saída, 156 00:09:45,840 --> 00:09:48,980 que neste caso é o noso bloque 1000-char. 157 00:09:48,980 --> 00:09:53,370 Un buffer overflow ocorre cando os datos son gravados tras o fin do bloque. 158 00:09:53,370 --> 00:09:57,790 >> Por exemplo, se un usuario fai realmente tipo en máis de 1.000 caracteres. 159 00:09:57,790 --> 00:10:01,570 Podes probar esta accidentalmente durante a programación con matrices. 160 00:10:01,570 --> 00:10:05,620 Se vostede ten unha matriz de 10 ints, nada impide vostede de tentar ler ou escribir 161 00:10:05,620 --> 00:10:07,810 o int 15. 162 00:10:07,810 --> 00:10:10,000 Non hai avisos do compilador ou erros. 163 00:10:10,000 --> 00:10:13,250 O programa só erros para adiante e accede á memoria 164 00:10:13,250 --> 00:10:18,150 onde pensa o int 15 será, e iso pode substituír as outras variables. 165 00:10:18,150 --> 00:10:22,040 No peor dos casos, pode substituír algúns dos interna do seu programa 166 00:10:22,040 --> 00:10:26,820 mecanismos de control, facendo que o seu programa para realmente executar instrucións diferentes 167 00:10:26,820 --> 00:10:28,340 do que se pretendía. 168 00:10:28,340 --> 00:10:31,360 >> Agora, non é común para facelo accidentalmente, 169 00:10:31,360 --> 00:10:35,150 pero esta é unha técnica moi común que os bandidos usan para romper programas 170 00:10:35,150 --> 00:10:39,080 e poñer o código malicioso en computadores de outros. 171 00:10:39,080 --> 00:10:42,910 Polo tanto, non podemos só usar a nosa solución inxenua. 172 00:10:42,910 --> 00:10:45,590 Necesitamos un xeito de evitar que os nosos programas de ser vulnerable 173 00:10:45,590 --> 00:10:47,880 a un ataque de estourido de buffer. 174 00:10:47,880 --> 00:10:51,430 Para facer isto, cómpre ter seguro de que o noso buffer pode crecer como lemos 175 00:10:51,430 --> 00:10:53,850 máis a entrada do usuario. 176 00:10:53,850 --> 00:10:57,440 A solución? Usamos un buffer alocado heap. 177 00:10:57,440 --> 00:10:59,950 Desde que se redimensioná-lo usando o redimensionamento da función realloc, 178 00:10:59,950 --> 00:11:04,580 e manter o control de dous números - o índice do próximo slot baleiro no buffer 179 00:11:04,580 --> 00:11:08,390 é a lonxitude ou a capacidade da memoria intermedia. 180 00:11:08,390 --> 00:11:13,210 Lemos en carácteres de usuario de cada vez usando a función fgetc. 181 00:11:13,210 --> 00:11:19,360 O argumento da función fgetc leva - stdin - é unha referencia para a cadea de entrada estándar, 182 00:11:19,360 --> 00:11:23,810 que é unha canle de entrada pre-conectado que é usado para transferir a entrada do usuario 183 00:11:23,810 --> 00:11:26,270 a partir do terminal para o programa. 184 00:11:26,270 --> 00:11:29,890 >> Sempre que o usuario escribe un novo personaxe, imos comprobar a ver se o índice 185 00:11:29,890 --> 00:11:35,810 da rañura libre seguinte máis 1 é maior que a capacidade da memoria intermedia. 186 00:11:35,810 --> 00:11:39,690 A unha, porque se trata o seguinte contido libre e 5, 187 00:11:39,690 --> 00:11:44,150 entón lonxitude noso buffer debe ser de 6 a 0 grazas a indexación. 188 00:11:44,150 --> 00:11:48,350 Se ficar sen espazo no buffer, entón intentamos redimensioná-la, 189 00:11:48,350 --> 00:11:51,690 dobrando-o para que podemos reducir o número de veces que redimensionar 190 00:11:51,690 --> 00:11:54,760 se o usuario está escribindo unha secuencia moi longa. 191 00:11:54,760 --> 00:11:57,950 A secuencia quedou moito tempo ou se queda sen memoria heap, 192 00:11:57,950 --> 00:12:01,350 que liberar noso buffer e nulos retorno. 193 00:12:01,350 --> 00:12:04,170 >> Por fin, engada o char para o buffer. 194 00:12:04,170 --> 00:12:08,200 Unha vez que os golpes de usuario entrar ou volver, sinalizando unha nova liña, 195 00:12:08,200 --> 00:12:12,050 ou especial char - control d - o que indica un fin de insumos, 196 00:12:12,050 --> 00:12:16,240 facemos unha comprobación para ver se o usuario realmente escribiu algo. 197 00:12:16,240 --> 00:12:18,820 Se non, imos voltar nulo. 198 00:12:18,820 --> 00:12:22,280 Se non, porque o noso tapón pode ser maior que necesitamos, 199 00:12:22,280 --> 00:12:24,830 no peor dos casos, é case dúas veces maior do que necesitamos 200 00:12:24,830 --> 00:12:27,830 sempre que dobrar cada vez que cambiar o tamaño, 201 00:12:27,830 --> 00:12:31,840 que facer unha nova copia da cadea usando só a cantidade de espazo que precisamos. 202 00:12:31,840 --> 00:12:34,220 Nós engadimos un 1 extra para a chamada malloc, 203 00:12:34,220 --> 00:12:37,810 de modo que non hai espazo para o carácter especial terminador nulo - o \ 0, 204 00:12:37,810 --> 00:12:41,990 que engadir a cadea, unha vez que copiar o resto dos personaxes, 205 00:12:41,990 --> 00:12:45,060 utilizando, en vez de strncpy strcpy 206 00:12:45,060 --> 00:12:48,830 de xeito que podemos especificar exactamente cantos caracteres que desexa copiar. 207 00:12:48,830 --> 00:12:51,690 Strcpy copia ata acadar un \ 0. 208 00:12:51,690 --> 00:12:55,740 Entón, liberar o noso buffer e devolver a copia para o chamador. 209 00:12:55,740 --> 00:12:59,840 >> Quen diría que unha simple función de aparencia pode ser tan complicado? 210 00:12:59,840 --> 00:13:02,820 Agora xa sabe o que vai á biblioteca CS50. 211 00:13:02,820 --> 00:13:06,470 >> O meu nome é Nate Hardison, e este é o CS50. 212 00:13:06,470 --> 00:13:08,350 [CS50.TV]