[Powered by Google Translate] [CS50 Library]
[Nate Hardison] [Harvard University]
[Esta es CS50. CS50.TV]
La biblioteca CS50 es una herramienta útil que nos hemos instalado en el aparato
para hacer más fácil para usted para escribir programas que los usuarios solicitan datos.
En este video, vamos a tirar de la cortina y mira lo que exactamente está en la biblioteca CS50.
>> En el video en bibliotecas C, hablamos de cómo # include archivos de jefes
de la biblioteca en el código fuente,
y luego conectarlo con un archivo de biblioteca binario durante la fase de vinculación
del proceso de compilación.
Los archivos de encabezado especificar la interfaz de la biblioteca.
Es decir, que detalle todos los recursos que la biblioteca tiene disponible para su uso,
como declaraciones de funciones, constantes y tipos de datos.
El archivo de biblioteca binario contiene la aplicación de la biblioteca,
que es una compilación de los archivos de cabecera de la biblioteca y las de la biblioteca. c archivos de código fuente.
>> El archivo de biblioteca binaria no es muy interesante de ver, ya que es, bueno, en binario.
Por lo tanto, vamos a echar un vistazo a los archivos de cabecera para la biblioteca en su lugar.
En este caso, sólo hay un archivo de cabecera llamado cs50.h.
Lo hemos instalado en el directorio de usuario incluyen
junto con los archivos de cabecera de las bibliotecas de otros sistemas.
>> Una de las primeras cosas que notará es que cs50.h # incluye los archivos de cabecera de otras bibliotecas -
float, bool límites, estándar, estándar y lib.
Una vez más, siguiendo el principio de no reinventar la rueda,
hemos construido la biblioteca CS0 el uso de herramientas que proporcionan otras para nosotros.
>> Lo siguiente que veremos en la biblioteca es que se define un nuevo tipo llamado "cuerda".
Esta línea realmente sólo crea un alias para el tipo char *,
por lo que no vuelve imbuir al nuevo tipo de cadena con atributos
comúnmente asociados con objetos de cadena en otros idiomas,
tales como la longitud.
La razón por la que hacemos esto es para proteger a los nuevos programadores de los detalles sangrientos
de los punteros hasta que estén listos.
>> La siguiente parte del archivo de encabezado es la declaración de las funciones
que la biblioteca CS50 proporciona junto con la documentación.
Observe el nivel de detalle en los comentarios aquí.
Esto es súper importante para que la gente sepa cómo utilizar estas funciones.
Declaramos que, a su vez, funciona para solicitar al usuario y caracteres de retorno, dobles, carrozas, enteros,
largo anhela, y las cuerdas, utilizando nuestro propio tipo de cadena.
Siguiendo el principio de ocultación de información,
hemos puesto nuestra definición en un archivo de ejecución separado c -. cs50.c--
ubicado en el directorio de origen del usuario.
Hemos proporcionado ese archivo para que usted pueda echar un vistazo a él,
aprender de ella, y recompilarlo en máquinas diferentes si lo desea,
a pesar de que creo que es mejor trabajar en el aparato de esta clase.
De todos modos, vamos a echar un vistazo ahora.
>> Las funciones getchar, GetDouble GetFloat, getInt, y GetLongLong
se construyen en la parte superior de la función GetString.
Resulta que todos siguen esencialmente el mismo patrón.
Ellos utilizan un bucle while para solicitar al usuario una línea de entrada.
Ellos devuelven un valor especial si el usuario introduce una línea vacía.
Se intenta analizar la entrada del usuario como el tipo adecuado,
ya sea un char, un doble, un flotador, etc
Y entonces o bien devolver el resultado si la entrada se ha analizado correctamente
o reprompt el usuario.
>> A un alto nivel, no hay nada realmente difícil aquí.
Es posible que haya escrito el código de estructura similar a ti mismo en el pasado.
Tal vez la parte más secreta de aspecto es la llamada sscanf que analiza la entrada del usuario.
Sscanf es parte de la familia de entrada de conversión de formato.
Vive en io.h estándar, y su trabajo consiste en analizar una cadena C,
según un formato determinado, el almacenamiento de los resultados de análisis en variables
proporcionado por el llamador.
Dado que las funciones de conversión de formato de entrada son muy útiles, las funciones utilizadas
que no son súper intuitivo al principio,
vamos a repasar cómo funciona sscanf.
>> El primer argumento de sscanf es un char * - un puntero a un carácter.
Para que la función funcione correctamente,
que el carácter debe ser el primer carácter de una cadena C,
terminó con el nulo carácter \ 0.
Esta es la cadena para analizar
El segundo argumento de sscanf es una cadena de formato,
normalmente pasa como una cadena constante,
y que podría haber visto una cadena como esta antes cuando se utiliza printf.
Un signo de porcentaje en la cadena de formato indica un especificador de conversión.
El carácter inmediatamente después de un signo de porcentaje,
indica el tipo C que queremos sscanf convertir.
En getInt, se ve que hay un d% y un% c.
Esto significa que sscanf tratará de un decimal int - el% d - y char a - la c%.
Para cada indicador de conversión en la cadena de formato,
sscanf espera un argumento correspondiente más adelante en la lista de parámetros.
Este argumento debe apuntar a una ubicación apropiada con tipo
en el que almacenar el resultado de la conversión.
>> La forma típica de hacer esto es crear una variable en la pila antes de la llamada sscanf
para cada elemento que desea analizar de la cadena
a continuación, utilice el operador de dirección - el signo - para pasar punteros
a las variables de la llamada sscanf.
Se puede ver que en getInt que hacer exactamente esto.
Justo antes de la llamada sscanf, declaramos un entero llamado n y aire llamada carbón en la pila,
y pasamos punteros a ellos en la llamada sscanf.
Poniendo estas variables en la pila es preferible a utilizar el espacio asignado
en el montón con malloc, ya que se evita la sobrecarga de la llamada malloc,
y usted no tiene que preocuparse por fugas de memoria.
Los personajes no precedidos por un signo de porcentaje no solicitan la conversión.
Más bien, sólo tiene que añadir a la especificación de formato.
>> Por ejemplo, si la cadena de formato en getint eran un d% en lugar,
sscanf buscaría la letra A seguida de un int,
y si bien se intentará convertir el int, no hacer nada más con el a.
La única excepción a esto es el espacio en blanco.
Los espacios en blanco en la cadena de formato coincide con ninguna cantidad de espacios en blanco -
incluso ninguno en absoluto.
Así que, por eso el comentario menciona posiblemente con la dirección y / o espacios en blanco.
Por lo tanto, en este momento parece que nuestra llamada sscanf tratará de analizar la cadena de entrada del usuario
mediante la comprobación de posibles espacios en blanco,
seguido de un int que se convierte y se almacena en la variable int n
seguido por una cierta cantidad de espacio en blanco, y seguido por un carácter
almacenado en la variable c char.
>> ¿Qué pasa con el valor de retorno?
Sscanf analizará la línea de entrada de principio a fin,
se detiene cuando alcanza el final o cuando un personaje en la entrada
no coincide con un carácter de formato o cuando no se puede hacer una conversión.
Su valor de retorno se utiliza para distinguir cuando se detuvo.
Si se detuvo, porque ha alcanzado el final de la cadena de entrada
antes de realizar las conversiones y antes de fallar para que coincida con parte de la cadena de formato,
entonces la constante EOF especial es devuelto.
De lo contrario, devuelve el número de conversiones exitosas,
que puede ser 0, 1 ó 2, ya que hemos pedido dos conversiones.
En nuestro caso, queremos asegurarnos de que el usuario escribió en un entero y sólo un int.
>> Por lo tanto, queremos volver a sscanf 1. Vea por qué?
Si sscanf devuelto 0, entonces no hay conversiones se hicieron,
por lo que el usuario ha escrito algo distinto de un int al comienzo de la entrada.
Si sscanf devuelve 2, entonces el usuario tuvo debidamente escríbala en el comienzo de la entrada,
pero después se pasan en algún carácter que no sea espacio en blanco después
ya que el% c conversión tuvo éxito.
Wow, eso es bastante larga explicación para una llamada a la función.
De todas formas, si quieres más información sobre sscanf y sus hermanos,
echa un vistazo a las páginas de manual, de Google, o ambos.
Hay un montón de opciones de formato de cadena,
y estos pueden ahorrar una gran cantidad de mano de obra cuando se trata de analizar cadenas en C.
>> La última función en la biblioteca para tener en cuenta es GetString.
Resulta que GetString es una función difícil de escribir correctamente,
a pesar de que parece una tarea sencilla, común.
¿Por qué es este el caso?
Bueno, vamos a pensar en cómo vamos a guardar la línea que el usuario escribe pulg
Dado que una cadena es una secuencia de caracteres,
lo que se quiere almacenar en una matriz en la pila,
pero necesitaríamos saber cuánto tiempo la matriz va a ser cuando lo declare.
Del mismo modo, si queremos ponerlo en el montón,
tenemos que pasar a malloc el número de bytes que desea reservar,
pero esto es imposible.
No tenemos idea de cómo muchos caracteres que el usuario escriba en
antes de que el usuario realmente se los escribe.
>> Una solución ingenua a este problema es simplemente para reservar una gran parte del espacio, por ejemplo,
un bloque de 1000 caracteres para la entrada del usuario,
suponiendo que el usuario nunca debería escribir en una cadena larga.
Esta es una mala idea por dos razones.
En primer lugar, en el supuesto de que los usuarios no suelen escribir en las cadenas de tanto tiempo,
usted podría perder una gran cantidad de memoria.
En las máquinas modernas, esto podría no ser un problema si usted hace esto
en uno o dos casos aislados,
pero si usted está tomando la entrada del usuario en un bucle y almacenar para su uso posterior,
usted puede absorber una tonelada de memoria.
Además, si el programa que se está escribiendo es para un equipo más pequeño -
un dispositivo como un teléfono inteligente o algo más con memoria limitada -
esta solución va a causar problemas mucho más rápido.
La segunda razón, más grave para no hacerlo es que deja su programa vulnerable
a lo que se llama un ataque de desbordamiento de búfer.
En programación, un buffer es la memoria utilizada para almacenar temporalmente los datos de entrada o salida,
que en este caso es nuestro 1000-carbón bloque.
Un desbordamiento de memoria se produce cuando los datos se escriben más allá del final del bloque.
>> Por ejemplo, si un usuario hace realmente tipo en más de 1000 caracteres.
Es posible que haya tenido este accidente cuando se programa con matrices.
Si usted tiene un arreglo de 10 enteros, nada le impide intentar leer o escribir
el int 15.
No hay advertencias o errores del compilador.
El programa simplemente errores de frente y tiene acceso a la memoria
donde se cree que el int 15a será, y esto puede sobrescribir otras variables.
En el peor de los casos, puede sobrescribir algunos de los internos de su programa
mecanismos de control, la causa de su programa a ejecutar realmente diferentes instrucciones
que usted pretende.
>> Ahora, no es común hacer esto por accidente,
pero esta es una técnica bastante común que los malos usan para romper programas
y poner el código malicioso en los ordenadores de otras personas.
Por lo tanto, nosotros no podemos usar nuestra solución ingenua.
Necesitamos una forma de evitar que nuestros programas de ser vulnerable
a un ataque de desbordamiento de búfer.
Para ello, tenemos que asegurarnos de que nuestro buffer puede crecer a medida que leemos
más información del usuario.
¿La solución? Usamos un montón búfer asignado.
Dado que podemos cambiar su tamaño utilizando el cambio de tamaño de la función realloc,
y hacemos un seguimiento de dos números - el índice de la ranura vacía siguiente en el búfer
y la longitud o la capacidad de la memoria intermedia.
Leemos en caracteres desde el usuario a la vez utilizando la función fgetc.
El argumento de la función de toma fgetc - stdin - es una referencia a la cadena de entrada estándar,
que es un canal de entrada preconectado que se utiliza para transferir la entrada del usuario
desde el terminal al programa.
>> Cada vez que el usuario escribe en un nuevo personaje, comprobamos si el índice
de la ranura libre siguiente más 1 es mayor que la capacidad de la memoria intermedia.
El uno entra porque si el índice gratis al lado es de 5,
pues el largo nuestro buffer debe ser de 6 a 0 gracias indexación.
Si nos hemos quedado sin espacio en el búfer, entonces intentamos cambiar su tamaño,
doblándolo de modo que reducir el número de veces que se cambia el tamaño
si el usuario está escribiendo en una cadena muy larga.
Si la cadena se ha hecho demasiado largo o si se queda sin memoria heap,
liberamos nuestro buffer y nulo retorno.
>> Por último, añadimos el char a la memoria intermedia.
Una vez que los accesos de usuario ingresar o devolver, señalando una nueva línea,
o el especial de caracteres - Control d - lo que indica un final de la entrada,
hacemos una comprobación para ver si el usuario realmente escribió en nada en absoluto.
Si no, devuelve null.
De lo contrario, ya que nuestro buffer es probablemente mayor de lo que necesitamos,
en el peor de los casos es casi dos veces tan grande como es necesario
ya que se duplica cada vez que cambia el tamaño,
se hace una nueva copia de la cadena con la cantidad de espacio que necesitamos.
Añadimos un extra de 1 a la llamada malloc,
por lo que hay espacio para el especial carácter nulo terminador - el \ 0,
que añadir a la cadena una vez que copiar en el resto de los caracteres,
utilizando strncpy en lugar de strcpy
de modo que podemos especificar exactamente cómo muchos caracteres que desea copiar.
Strcpy copia hasta que llega a \ 0.
Entonces liberamos nuestro buffer y devolver la copia a la persona que llama.
>> ¿Quién sabía que esa función aparentemente simple podría ser tan complicado?
Ahora usted sabe lo que entra en la biblioteca CS50.
>> Mi nombre es Nate Hardison, y esto es CS50.
[CS50.TV]