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 [Esta es CS50. CS50.TV] 4 00:00:07,260 --> 00:00:11,510 La biblioteca CS50 es una herramienta útil que nos hemos instalado en el aparato 5 00:00:11,510 --> 00:00:15,870 para hacer más fácil para usted para escribir programas que los usuarios solicitan datos. 6 00:00:15,870 --> 00:00:21,670 En este video, vamos a tirar de la cortina y mira lo que exactamente está en la biblioteca CS50. 7 00:00:21,670 --> 00:00:25,520 >> En el video en bibliotecas C, hablamos de cómo # include archivos de jefes 8 00:00:25,520 --> 00:00:27,570 de la biblioteca en el código fuente, 9 00:00:27,570 --> 00:00:31,150 y luego conectarlo con un archivo de biblioteca binario durante la fase de vinculación 10 00:00:31,150 --> 00:00:33,140 del proceso de compilación. 11 00:00:33,140 --> 00:00:36,440 Los archivos de encabezado especificar la interfaz de la biblioteca. 12 00:00:36,440 --> 00:00:41,280 Es decir, que detalle todos los recursos que la biblioteca tiene disponible para su uso, 13 00:00:41,280 --> 00:00:45,250 como declaraciones de funciones, constantes y tipos de datos. 14 00:00:45,250 --> 00:00:48,890 El archivo de biblioteca binario contiene la aplicación de la biblioteca, 15 00:00:48,890 --> 00:00:54,580 que es una compilación de los archivos de cabecera de la biblioteca y las de la biblioteca. c archivos de código fuente. 16 00:00:54,580 --> 00:00:59,820 >> El archivo de biblioteca binaria no es muy interesante de ver, ya que es, bueno, en binario. 17 00:00:59,820 --> 00:01:03,300 Por lo tanto, vamos a echar un vistazo a los archivos de cabecera para la biblioteca en su lugar. 18 00:01:03,300 --> 00:01:07,710 En este caso, sólo hay un archivo de cabecera llamado cs50.h. 19 00:01:07,710 --> 00:01:11,040 Lo hemos instalado en el directorio de usuario incluyen 20 00:01:11,040 --> 00:01:15,150 junto con los archivos de cabecera de las bibliotecas de otros sistemas. 21 00:01:15,150 --> 00:01:21,530 >> Una de las primeras cosas que notará es que cs50.h # incluye los archivos de cabecera de otras bibliotecas - 22 00:01:21,530 --> 00:01:25,670 float, bool límites, estándar, estándar y lib. 23 00:01:25,670 --> 00:01:28,800 Una vez más, siguiendo el principio de no reinventar la rueda, 24 00:01:28,800 --> 00:01:33,490 hemos construido la biblioteca CS0 el uso de herramientas que proporcionan otras para nosotros. 25 00:01:33,490 --> 00:01:38,690 >> Lo siguiente que veremos en la biblioteca es que se define un nuevo tipo llamado "cuerda". 26 00:01:38,690 --> 00:01:42,330 Esta línea realmente sólo crea un alias para el tipo char *, 27 00:01:42,330 --> 00:01:46,000 por lo que no vuelve imbuir al nuevo tipo de cadena con atributos 28 00:01:46,000 --> 00:01:49,650 comúnmente asociados con objetos de cadena en otros idiomas, 29 00:01:49,650 --> 00:01:50,850 tales como la longitud. 30 00:01:50,850 --> 00:01:55,180 La razón por la que hacemos esto es para proteger a los nuevos programadores de los detalles sangrientos 31 00:01:55,180 --> 00:01:57,580 de los punteros hasta que estén listos. 32 00:01:57,580 --> 00:02:00,130 >> La siguiente parte del archivo de encabezado es la declaración de las funciones 33 00:02:00,130 --> 00:02:04,410 que la biblioteca CS50 proporciona junto con la documentación. 34 00:02:04,410 --> 00:02:06,940 Observe el nivel de detalle en los comentarios aquí. 35 00:02:06,940 --> 00:02:10,560 Esto es súper importante para que la gente sepa cómo utilizar estas funciones. 36 00:02:10,560 --> 00:02:19,150 Declaramos que, a su vez, funciona para solicitar al usuario y caracteres de retorno, dobles, carrozas, enteros, 37 00:02:19,150 --> 00:02:24,160 largo anhela, y las cuerdas, utilizando nuestro propio tipo de cadena. 38 00:02:24,160 --> 00:02:26,260 Siguiendo el principio de ocultación de información, 39 00:02:26,260 --> 00:02:31,640 hemos puesto nuestra definición en un archivo de ejecución separado c -. cs50.c-- 40 00:02:31,640 --> 00:02:35,110 ubicado en el directorio de origen del usuario. 41 00:02:35,110 --> 00:02:38,040 Hemos proporcionado ese archivo para que usted pueda echar un vistazo a él, 42 00:02:38,040 --> 00:02:41,490 aprender de ella, y recompilarlo en máquinas diferentes si lo desea, 43 00:02:41,490 --> 00:02:45,510 a pesar de que creo que es mejor trabajar en el aparato de esta clase. 44 00:02:45,510 --> 00:02:47,580 De todos modos, vamos a echar un vistazo ahora. 45 00:02:49,020 --> 00:02:54,620 >> Las funciones getchar, GetDouble GetFloat, getInt, y GetLongLong 46 00:02:54,620 --> 00:02:58,160 se construyen en la parte superior de la función GetString. 47 00:02:58,160 --> 00:03:01,510 Resulta que todos siguen esencialmente el mismo patrón. 48 00:03:01,510 --> 00:03:04,870 Ellos utilizan un bucle while para solicitar al usuario una línea de entrada. 49 00:03:04,870 --> 00:03:08,430 Ellos devuelven un valor especial si el usuario introduce una línea vacía. 50 00:03:08,430 --> 00:03:11,750 Se intenta analizar la entrada del usuario como el tipo adecuado, 51 00:03:11,750 --> 00:03:15,010 ya sea un char, un doble, un flotador, etc 52 00:03:15,010 --> 00:03:18,710 Y entonces o bien devolver el resultado si la entrada se ha analizado correctamente 53 00:03:18,710 --> 00:03:21,330 o reprompt el usuario. 54 00:03:21,330 --> 00:03:24,230 >> A un alto nivel, no hay nada realmente difícil aquí. 55 00:03:24,230 --> 00:03:28,760 Es posible que haya escrito el código de estructura similar a ti mismo en el pasado. 56 00:03:28,760 --> 00:03:34,720 Tal vez la parte más secreta de aspecto es la llamada sscanf que analiza la entrada del usuario. 57 00:03:34,720 --> 00:03:38,160 Sscanf es parte de la familia de entrada de conversión de formato. 58 00:03:38,160 --> 00:03:42,300 Vive en io.h estándar, y su trabajo consiste en analizar una cadena C, 59 00:03:42,300 --> 00:03:46,520 según un formato determinado, el almacenamiento de los resultados de análisis en variables 60 00:03:46,520 --> 00:03:48,720 proporcionado por el llamador. 61 00:03:48,720 --> 00:03:53,570 Dado que las funciones de conversión de formato de entrada son muy útiles, las funciones utilizadas 62 00:03:53,570 --> 00:03:56,160 que no son súper intuitivo al principio, 63 00:03:56,160 --> 00:03:58,300 vamos a repasar cómo funciona sscanf. 64 00:03:58,300 --> 00:04:03,330 >> El primer argumento de sscanf es un char * - un puntero a un carácter. 65 00:04:03,330 --> 00:04:05,150 Para que la función funcione correctamente, 66 00:04:05,150 --> 00:04:08,340 que el carácter debe ser el primer carácter de una cadena C, 67 00:04:08,340 --> 00:04:12,270 terminó con el nulo carácter \ 0. 68 00:04:12,270 --> 00:04:15,120 Esta es la cadena para analizar 69 00:04:15,120 --> 00:04:18,269 El segundo argumento de sscanf es una cadena de formato, 70 00:04:18,269 --> 00:04:20,839 normalmente pasa como una cadena constante, 71 00:04:20,839 --> 00:04:24,040 y que podría haber visto una cadena como esta antes cuando se utiliza printf. 72 00:04:24,040 --> 00:04:28,650 Un signo de porcentaje en la cadena de formato indica un especificador de conversión. 73 00:04:28,650 --> 00:04:30,850 El carácter inmediatamente después de un signo de porcentaje, 74 00:04:30,850 --> 00:04:35,430 indica el tipo C que queremos sscanf convertir. 75 00:04:35,430 --> 00:04:40,090 En getInt, se ve que hay un d% y un% c. 76 00:04:40,090 --> 00:04:48,690 Esto significa que sscanf tratará de un decimal int - el% d - y char a - la c%. 77 00:04:48,690 --> 00:04:51,510 Para cada indicador de conversión en la cadena de formato, 78 00:04:51,510 --> 00:04:56,620 sscanf espera un argumento correspondiente más adelante en la lista de parámetros. 79 00:04:56,620 --> 00:05:00,850 Este argumento debe apuntar a una ubicación apropiada con tipo 80 00:05:00,850 --> 00:05:04,000 en el que almacenar el resultado de la conversión. 81 00:05:04,000 --> 00:05:08,910 >> La forma típica de hacer esto es crear una variable en la pila antes de la llamada sscanf 82 00:05:08,910 --> 00:05:11,440 para cada elemento que desea analizar de la cadena 83 00:05:11,440 --> 00:05:15,520 a continuación, utilice el operador de dirección - el signo - para pasar punteros 84 00:05:15,520 --> 00:05:19,100 a las variables de la llamada sscanf. 85 00:05:19,100 --> 00:05:22,720 Se puede ver que en getInt que hacer exactamente esto. 86 00:05:22,720 --> 00:05:28,240 Justo antes de la llamada sscanf, declaramos un entero llamado n y aire llamada carbón en la pila, 87 00:05:28,240 --> 00:05:32,340 y pasamos punteros a ellos en la llamada sscanf. 88 00:05:32,340 --> 00:05:35,800 Poniendo estas variables en la pila es preferible a utilizar el espacio asignado 89 00:05:35,800 --> 00:05:39,350 en el montón con malloc, ya que se evita la sobrecarga de la llamada malloc, 90 00:05:39,350 --> 00:05:43,060 y usted no tiene que preocuparse por fugas de memoria. 91 00:05:43,060 --> 00:05:47,280 Los personajes no precedidos por un signo de porcentaje no solicitan la conversión. 92 00:05:47,280 --> 00:05:50,380 Más bien, sólo tiene que añadir a la especificación de formato. 93 00:05:50,380 --> 00:05:56,500 >> Por ejemplo, si la cadena de formato en getint eran un d% en lugar, 94 00:05:56,500 --> 00:05:59,800 sscanf buscaría la letra A seguida de un int, 95 00:05:59,800 --> 00:06:04,360 y si bien se intentará convertir el int, no hacer nada más con el a. 96 00:06:04,360 --> 00:06:07,440 La única excepción a esto es el espacio en blanco. 97 00:06:07,440 --> 00:06:11,030 Los espacios en blanco en la cadena de formato coincide con ninguna cantidad de espacios en blanco - 98 00:06:11,030 --> 00:06:12,890 incluso ninguno en absoluto. 99 00:06:12,890 --> 00:06:18,100 Así que, por eso el comentario menciona posiblemente con la dirección y / o espacios en blanco. 100 00:06:18,100 --> 00:06:22,910 Por lo tanto, en este momento parece que nuestra llamada sscanf tratará de analizar la cadena de entrada del usuario 101 00:06:22,910 --> 00:06:25,380 mediante la comprobación de posibles espacios en blanco, 102 00:06:25,380 --> 00:06:29,300 seguido de un int que se convierte y se almacena en la variable int n 103 00:06:29,300 --> 00:06:33,090 seguido por una cierta cantidad de espacio en blanco, y seguido por un carácter 104 00:06:33,090 --> 00:06:35,810 almacenado en la variable c char. 105 00:06:35,810 --> 00:06:37,790 >> ¿Qué pasa con el valor de retorno? 106 00:06:37,790 --> 00:06:41,560 Sscanf analizará la línea de entrada de principio a fin, 107 00:06:41,560 --> 00:06:44,860 se detiene cuando alcanza el final o cuando un personaje en la entrada 108 00:06:44,860 --> 00:06:49,320 no coincide con un carácter de formato o cuando no se puede hacer una conversión. 109 00:06:49,320 --> 00:06:52,690 Su valor de retorno se utiliza para distinguir cuando se detuvo. 110 00:06:52,690 --> 00:06:55,670 Si se detuvo, porque ha alcanzado el final de la cadena de entrada 111 00:06:55,670 --> 00:07:00,630 antes de realizar las conversiones y antes de fallar para que coincida con parte de la cadena de formato, 112 00:07:00,630 --> 00:07:04,840 entonces la constante EOF especial es devuelto. 113 00:07:04,840 --> 00:07:08,200 De lo contrario, devuelve el número de conversiones exitosas, 114 00:07:08,200 --> 00:07:14,380 que puede ser 0, 1 ó 2, ya que hemos pedido dos conversiones. 115 00:07:14,380 --> 00:07:19,000 En nuestro caso, queremos asegurarnos de que el usuario escribió en un entero y sólo un int. 116 00:07:19,000 --> 00:07:23,370 >> Por lo tanto, queremos volver a sscanf 1. Vea por qué? 117 00:07:23,370 --> 00:07:26,850 Si sscanf devuelto 0, entonces no hay conversiones se hicieron, 118 00:07:26,850 --> 00:07:31,690 por lo que el usuario ha escrito algo distinto de un int al comienzo de la entrada. 119 00:07:31,690 --> 00:07:37,100 Si sscanf devuelve 2, entonces el usuario tuvo debidamente escríbala en el comienzo de la entrada, 120 00:07:37,100 --> 00:07:41,390 pero después se pasan en algún carácter que no sea espacio en blanco después 121 00:07:41,390 --> 00:07:44,940 ya que el% c conversión tuvo éxito. 122 00:07:44,940 --> 00:07:49,570 Wow, eso es bastante larga explicación para una llamada a la función. 123 00:07:49,570 --> 00:07:53,460 De todas formas, si quieres más información sobre sscanf y sus hermanos, 124 00:07:53,460 --> 00:07:57,130 echa un vistazo a las páginas de manual, de Google, o ambos. 125 00:07:57,130 --> 00:07:58,780 Hay un montón de opciones de formato de cadena, 126 00:07:58,780 --> 00:08:03,830 y estos pueden ahorrar una gran cantidad de mano de obra cuando se trata de analizar cadenas en C. 127 00:08:03,830 --> 00:08:07,180 >> La última función en la biblioteca para tener en cuenta es GetString. 128 00:08:07,180 --> 00:08:10,310 Resulta que GetString es una función difícil de escribir correctamente, 129 00:08:10,310 --> 00:08:14,290 a pesar de que parece una tarea sencilla, común. 130 00:08:14,290 --> 00:08:16,170 ¿Por qué es este el caso? 131 00:08:16,170 --> 00:08:21,380 Bueno, vamos a pensar en cómo vamos a guardar la línea que el usuario escribe pulg 132 00:08:21,380 --> 00:08:23,880 Dado que una cadena es una secuencia de caracteres, 133 00:08:23,880 --> 00:08:26,430 lo que se quiere almacenar en una matriz en la pila, 134 00:08:26,430 --> 00:08:31,250 pero necesitaríamos saber cuánto tiempo la matriz va a ser cuando lo declare. 135 00:08:31,250 --> 00:08:34,030 Del mismo modo, si queremos ponerlo en el montón, 136 00:08:34,030 --> 00:08:38,090 tenemos que pasar a malloc el número de bytes que desea reservar, 137 00:08:38,090 --> 00:08:39,730 pero esto es imposible. 138 00:08:39,730 --> 00:08:42,760 No tenemos idea de cómo muchos caracteres que el usuario escriba en 139 00:08:42,760 --> 00:08:46,590 antes de que el usuario realmente se los escribe. 140 00:08:46,590 --> 00:08:50,720 >> Una solución ingenua a este problema es simplemente para reservar una gran parte del espacio, por ejemplo, 141 00:08:50,720 --> 00:08:54,540 un bloque de 1000 caracteres para la entrada del usuario, 142 00:08:54,540 --> 00:08:57,980 suponiendo que el usuario nunca debería escribir en una cadena larga. 143 00:08:57,980 --> 00:09:00,810 Esta es una mala idea por dos razones. 144 00:09:00,810 --> 00:09:05,280 En primer lugar, en el supuesto de que los usuarios no suelen escribir en las cadenas de tanto tiempo, 145 00:09:05,280 --> 00:09:07,610 usted podría perder una gran cantidad de memoria. 146 00:09:07,610 --> 00:09:10,530 En las máquinas modernas, esto podría no ser un problema si usted hace esto 147 00:09:10,530 --> 00:09:13,890 en uno o dos casos aislados, 148 00:09:13,890 --> 00:09:17,630 pero si usted está tomando la entrada del usuario en un bucle y almacenar para su uso posterior, 149 00:09:17,630 --> 00:09:20,870 usted puede absorber una tonelada de memoria. 150 00:09:20,870 --> 00:09:24,450 Además, si el programa que se está escribiendo es para un equipo más pequeño - 151 00:09:24,450 --> 00:09:28,100 un dispositivo como un teléfono inteligente o algo más con memoria limitada - 152 00:09:28,100 --> 00:09:32,060 esta solución va a causar problemas mucho más rápido. 153 00:09:32,060 --> 00:09:36,450 La segunda razón, más grave para no hacerlo es que deja su programa vulnerable 154 00:09:36,450 --> 00:09:39,710 a lo que se llama un ataque de desbordamiento de búfer. 155 00:09:39,710 --> 00:09:45,840 En programación, un buffer es la memoria utilizada para almacenar temporalmente los datos de entrada o salida, 156 00:09:45,840 --> 00:09:48,980 que en este caso es nuestro 1000-carbón bloque. 157 00:09:48,980 --> 00:09:53,370 Un desbordamiento de memoria se produce cuando los datos se escriben más allá del final del bloque. 158 00:09:53,370 --> 00:09:57,790 >> Por ejemplo, si un usuario hace realmente tipo en más de 1000 caracteres. 159 00:09:57,790 --> 00:10:01,570 Es posible que haya tenido este accidente cuando se programa con matrices. 160 00:10:01,570 --> 00:10:05,620 Si usted tiene un arreglo de 10 enteros, nada le impide intentar leer o escribir 161 00:10:05,620 --> 00:10:07,810 el int 15. 162 00:10:07,810 --> 00:10:10,000 No hay advertencias o errores del compilador. 163 00:10:10,000 --> 00:10:13,250 El programa simplemente errores de frente y tiene acceso a la memoria 164 00:10:13,250 --> 00:10:18,150 donde se cree que el int 15a será, y esto puede sobrescribir otras variables. 165 00:10:18,150 --> 00:10:22,040 En el peor de los casos, puede sobrescribir algunos de los internos de su programa 166 00:10:22,040 --> 00:10:26,820 mecanismos de control, la causa de su programa a ejecutar realmente diferentes instrucciones 167 00:10:26,820 --> 00:10:28,340 que usted pretende. 168 00:10:28,340 --> 00:10:31,360 >> Ahora, no es común hacer esto por accidente, 169 00:10:31,360 --> 00:10:35,150 pero esta es una técnica bastante común que los malos usan para romper programas 170 00:10:35,150 --> 00:10:39,080 y poner el código malicioso en los ordenadores de otras personas. 171 00:10:39,080 --> 00:10:42,910 Por lo tanto, nosotros no podemos usar nuestra solución ingenua. 172 00:10:42,910 --> 00:10:45,590 Necesitamos una forma de evitar que nuestros programas de ser vulnerable 173 00:10:45,590 --> 00:10:47,880 a un ataque de desbordamiento de búfer. 174 00:10:47,880 --> 00:10:51,430 Para ello, tenemos que asegurarnos de que nuestro buffer puede crecer a medida que leemos 175 00:10:51,430 --> 00:10:53,850 más información del usuario. 176 00:10:53,850 --> 00:10:57,440 ¿La solución? Usamos un montón búfer asignado. 177 00:10:57,440 --> 00:10:59,950 Dado que podemos cambiar su tamaño utilizando el cambio de tamaño de la función realloc, 178 00:10:59,950 --> 00:11:04,580 y hacemos un seguimiento de dos números - el índice de la ranura vacía siguiente en el búfer 179 00:11:04,580 --> 00:11:08,390 y la longitud o la capacidad de la memoria intermedia. 180 00:11:08,390 --> 00:11:13,210 Leemos en caracteres desde el usuario a la vez utilizando la función fgetc. 181 00:11:13,210 --> 00:11:19,360 El argumento de la función de toma fgetc - stdin - es una referencia a la cadena de entrada estándar, 182 00:11:19,360 --> 00:11:23,810 que es un canal de entrada preconectado que se utiliza para transferir la entrada del usuario 183 00:11:23,810 --> 00:11:26,270 desde el terminal al programa. 184 00:11:26,270 --> 00:11:29,890 >> Cada vez que el usuario escribe en un nuevo personaje, comprobamos si el índice 185 00:11:29,890 --> 00:11:35,810 de la ranura libre siguiente más 1 es mayor que la capacidad de la memoria intermedia. 186 00:11:35,810 --> 00:11:39,690 El uno entra porque si el índice gratis al lado es de 5, 187 00:11:39,690 --> 00:11:44,150 pues el largo nuestro buffer debe ser de 6 a 0 gracias indexación. 188 00:11:44,150 --> 00:11:48,350 Si nos hemos quedado sin espacio en el búfer, entonces intentamos cambiar su tamaño, 189 00:11:48,350 --> 00:11:51,690 doblándolo de modo que reducir el número de veces que se cambia el tamaño 190 00:11:51,690 --> 00:11:54,760 si el usuario está escribiendo en una cadena muy larga. 191 00:11:54,760 --> 00:11:57,950 Si la cadena se ha hecho demasiado largo o si se queda sin memoria heap, 192 00:11:57,950 --> 00:12:01,350 liberamos nuestro buffer y nulo retorno. 193 00:12:01,350 --> 00:12:04,170 >> Por último, añadimos el char a la memoria intermedia. 194 00:12:04,170 --> 00:12:08,200 Una vez que los accesos de usuario ingresar o devolver, señalando una nueva línea, 195 00:12:08,200 --> 00:12:12,050 o el especial de caracteres - Control d - lo que indica un final de la entrada, 196 00:12:12,050 --> 00:12:16,240 hacemos una comprobación para ver si el usuario realmente escribió en nada en absoluto. 197 00:12:16,240 --> 00:12:18,820 Si no, devuelve null. 198 00:12:18,820 --> 00:12:22,280 De lo contrario, ya que nuestro buffer es probablemente mayor de lo que necesitamos, 199 00:12:22,280 --> 00:12:24,830 en el peor de los casos es casi dos veces tan grande como es necesario 200 00:12:24,830 --> 00:12:27,830 ya que se duplica cada vez que cambia el tamaño, 201 00:12:27,830 --> 00:12:31,840 se hace una nueva copia de la cadena con la cantidad de espacio que necesitamos. 202 00:12:31,840 --> 00:12:34,220 Añadimos un extra de 1 a la llamada malloc, 203 00:12:34,220 --> 00:12:37,810 por lo que hay espacio para el especial carácter nulo terminador - el \ 0, 204 00:12:37,810 --> 00:12:41,990 que añadir a la cadena una vez que copiar en el resto de los caracteres, 205 00:12:41,990 --> 00:12:45,060 utilizando strncpy en lugar de strcpy 206 00:12:45,060 --> 00:12:48,830 de modo que podemos especificar exactamente cómo muchos caracteres que desea copiar. 207 00:12:48,830 --> 00:12:51,690 Strcpy copia hasta que llega a \ 0. 208 00:12:51,690 --> 00:12:55,740 Entonces liberamos nuestro buffer y devolver la copia a la persona que llama. 209 00:12:55,740 --> 00:12:59,840 >> ¿Quién sabía que esa función aparentemente simple podría ser tan complicado? 210 00:12:59,840 --> 00:13:02,820 Ahora usted sabe lo que entra en la biblioteca CS50. 211 00:13:02,820 --> 00:13:06,470 >> Mi nombre es Nate Hardison, y esto es CS50. 212 00:13:06,470 --> 00:13:08,350 [CS50.TV]