[Powered by Google Translate] [CS50 Biblioteca]
[Nate Hardison] [Harvard University]
[Esta é CS50. CS50.TV]
A biblioteca CS50 é unha ferramenta útil que temos instalado no aparello
para facelo máis doado para ti escribir programas que solicitan entrada dos usuarios.
Neste vídeo, imos tirar a cortina e ollar para o que é exactamente na biblioteca CS50.
>> No vídeo en bibliotecas C, falamos sobre como # include arquivos cabeceiras
da biblioteca no seu código fonte,
e entón conectar cun arquivo de biblioteca binario durante a fase de vinculación
do proceso de compilación.
Os ficheiros de cabeceira especificar a interface da biblioteca.
É dicir, eles detalladamente todos os recursos que a biblioteca ten dispoñible para usar,
como declaracións de funcións, constantes e tipos de datos.
O arquivo de biblioteca binario contén a implementación da biblioteca,
que está feita a partir de arquivos da biblioteca de cabeceira e as da biblioteca. c arquivos de código fonte.
>> O arquivo de biblioteca binario non é moi interesante para ollar xa que é, así, en binario.
Entón, imos dar un ollo nos arquivos de cabeceira para a biblioteca vez.
Neste caso, hai só un arquivo de cabeceira chamado cs50.h.
Nós instalado no usuario incluír o directorio
xunto cos arquivos das bibliotecas do sistema de outros cabeceira.
>> Unha das primeiras cousas que vai notar é que cs50.h # inclúe ficheiros de cabeceira doutras bibliotecas -
float, límites, patrón bool e lib estándar.
Unha vez máis, seguindo o principio de non reinventar a roda,
Nós construímos a biblioteca CS0 empregando as ferramentas que outros subministrados por nós.
>> A seguinte cousa que vai ver na biblioteca é que definimos un novo tipo chamado "cadea".
Esta liña realmente só crea un alias para o tipo char *,
para que non pase de máxica Inculcar o novo tipo de grupo con atributos
comunmente asociado con obxectos de cadea noutras linguas,
como a lonxitude.
A razón por que fixen isto é para protexer novos desenvolvedores de detalles
de punteiros ata que estean listos.
>> A seguinte parte do arquivo de cabeceira é a declaración das funcións
que a biblioteca CS50 proporciona, xunto coa documentación.
Teña en conta o nivel de detalle nos comentarios aquí.
Isto é super importante para que as persoas saben como usar estas funcións.
Nós declaramos, á súa vez, traballa para solicitar ao usuario e caracteres de retorno, dobres, coches alegóricos, ints,
tempo ansia, e cordas, usando o noso propio tipo cadea.
Seguindo o principio de ocultación de información,
temos que poñer a nosa definición nun ficheiro de implementación separada C -. cs50.c -
situado no directorio de orixe do autor.
Nós fornecen o ficheiro de modo que pode dar un ollo niso,
aprender con el, e recompilar en máquinas diferentes, se o desexa,
aínda que nós cremos que é mellor para traballar no dispositivo para esta clase.
En calquera caso, imos dar un ollo nel agora.
>> As funcións getchar, GetDouble, GetFloat, GetInt e GetLongLong
son todos construídos na parte superior da función GetString.
Acontece que todos eles seguen basicamente o mesmo patrón.
Eles usan un loop while para solicitar ao usuario unha liña de entrada.
Eles retornan un valor especial se o usuario inserir unha liña baleira.
Eles tratan de analizar a entrada do usuario como o tipo apropiado,
sexa un char, un dúo, un float, etc
E entón eles ou devolver o resultado a entrada foi analizado correctamente
ou eles Reprompt o usuario.
>> Nun nivel alto, non hai nada moi complicado aquí.
Podes escribir o código de estrutura semellante a si mesmo no pasado.
Quizais a parte máis enigmática para o futuro é a chamada sscanf que analiza a entrada do usuario.
Sscanf é parte da familia de conversión de entrada formato.
Vive en io.h estándar, eo seu traballo é analizar unha secuencia C,
de acordo cun formato específico, almacenando os resultados da análise da variable
fornecido polo chamador.
Dende a entrada de funcións de conversión de formato son moi útiles, as funcións amplamente utilizados
que non son super intuitivo en primeiro lugar,
nós falaremos sobre como sscanf funciona.
>> O primeiro argumento para sscanf é un char * - un punteiro para un personaxe.
Para a función de traballar correctamente,
ese carácter debe ser o primeiro carácter dunha cadea C,
rematou o nulo \ 0 personaxe.
Esta é a cadea para analizar
O segundo argumento para sscanf é unha secuencia de formato,
tipicamente pasado como unha constante cadea,
e pode ver unha secuencia como esta antes, cando usar printf.
Un sinal de porcentaxe no formato cadea indica un especificador de conversión.
O carácter inmediatamente despois un sinal de porcentaxe,
indica o tipo C que queremos sscanf para converter.
En GetInt, ve que hai unha d% e% c.
Isto significa que sscanf vai un int decimal - o% d - e un char - o c%.
Para cada especificador de conversión na cadea de formato,
sscanf espera un argumento correspondente máis tarde, na súa lista de argumentos.
Ese argumento debe apuntar a un lugar debidamente ingresaran
en que para almacenar o resultado da conversión.
>> A forma típica de facelo é crear unha variable na pila antes da chamada sscanf
para cada elemento que quere analizar a partir da cadea
e entón usar o operador de enderezo - o comercial - para pasar punteiros
a esas variables para a chamada sscanf.
Podes ver que en GetInt facemos exactamente isto.
Logo antes de chamada sscanf, nós declaramos un int chamado n e un c chamada char na pila,
e pasamos punteiros para eles na chamada sscanf.
Poñer esas variables na pila é preferible usar o espazo alocado
no heap con malloc, unha vez que evitar a sobrecarga da chamada malloc,
e non ten que se preocupar con baleirado de memoria.
Caracteres non prefixadas por un sinal de porcentaxe non solicitan a conversión.
En vez diso, pode engadir a especificación de formato.
>> Por exemplo, se a secuencia de formato en GetInt fose un d% en vez diso,
sscanf ía buscar a letra seguido dun int,
e mentres el tentaría converter o int, que non ía facer nada co un.
A única excepción a iso é espazo en branco.
Caracteres de espazo en branco na cadea formato de combinar con calquera cantidade de espazos en branco -
mesmo ningún.
Entón, é por iso que o comentario menciona posiblemente cos principais e / ou espazos en branco.
Entón, neste momento, parece que o noso chamamento sscanf intentará analizar a secuencia de entrada do usuario
comprobando posibles espazos en branco,
seguido dun int que ha ser convertida e almacenada na variable int n
seguido por unha certa cantidade de espazos en branco, e seguido por un carácter
almacenado na variable char c.
>> E sobre o valor de retorno?
Sscanf ha analizar a liña de entrada do principio ao final,
parando cando chega o final ou cando un carácter no input
non corresponde a un personaxe formato ou cando non pode facer unha conversión.
O seu valor de retorno é usado para illar cando parou.
Se parou, porque chegou ao fin da cadea de entrada
antes de facer as conversións e antes de deixar de coincidir con parte da cadea de formato,
a continuación, o EOF especial constante é retornado.
Se non, el retorna o número de conversións exitosa,
o que pode ser 0, 1 ou 2, unha vez que xa pediu dúas conversións.
No noso caso, queremos estar seguro de que o usuario introduciu un int e só un int.
>> Entón, nós queremos sscanf para voltar 1. Vexa por que?
Se sscanf retornado 0, entón non conversións foron feitas,
de xeito que o usuario escribiu algo diferente dun int no inicio da entrada.
Se sscanf retorna 2, entón o usuario non escribe correctamente no comezo da entrada,
pero entón escritas nalgúns caracteres non espazo en branco despois
unha vez que a% c conversión tivo éxito.
Guau, iso é unha longa explicación para unha chamada de función.
En fin, se queres máis información sobre sscanf e os seus irmáns,
Consulte as páxinas de manual, Google, ou ambos.
Hai moitas opcións de cadea de formato,
e estes poden aforrar-lle moito do traballo manual ao intentar analizar secuencias en C.
>> A última función na biblioteca é mirar para GetString.
Acontece que GetString é unha función difícil de escribir correctamente,
aínda que parece que tal unha tarefa sinxela, común.
Por que é este o caso?
Ben, imos pensar sobre como estamos indo para almacenar a liña que o usuario escribe Pol
Unha vez que unha cadea é unha secuencia de caracteres,
podemos querer almacena-lo nunha matriz na pila,
pero sería preciso saber canto tempo a matriz vai ser cando declara.
Do mesmo xeito, se queremos poñelas na pila,
necesitamos pasar malloc o número de bytes que queremos reserva,
pero isto é imposible.
Nós non temos idea de cantos caracteres que o usuario escriba
antes de que o usuario non escribe.
>> Unha solución inxenua para este problema é só reservar un anaco grande de espazo, por exemplo,
un bloque de 1000 caracteres para a entrada do usuario,
asumindo que o usuario nunca debería escribir unha secuencia de tanto tempo.
Esta é unha idea malo por dous motivos.
En primeiro lugar, asumindo que os usuarios normalmente non escribir en cordas moito tempo,
podería perder unha grande cantidade de memoria.
En máquinas modernas, isto pode non ser un problema se fai iso
en un ou dous casos illados,
pero se está tomando a entrada do usuario en un loop e almacenamento para o seu uso posterior,
pode rapidamente Sugar ata unha tonelada de memoria.
Ademais, o programa que está escribindo é un ordenador pequeno -
un dispositivo como un teléfono ou calquera outra cousa con memoria limitada -
esta solución pode causar problemas moito máis rápido.
O motivo, segundo máis grave non facer iso é que el deixa o seu programa vulnerable
para o que se denomina un ataque de estourido de buffer.
Na programación, un buffer de memoria é usado para almacenar temporalmente os datos de entrada ou de saída,
que neste caso é o noso bloque 1000-char.
Un buffer overflow ocorre cando os datos son gravados tras o fin do bloque.
>> Por exemplo, se un usuario fai realmente tipo en máis de 1.000 caracteres.
Podes probar esta accidentalmente durante a programación con matrices.
Se vostede ten unha matriz de 10 ints, nada impide vostede de tentar ler ou escribir
o int 15.
Non hai avisos do compilador ou erros.
O programa só erros para adiante e accede á memoria
onde pensa o int 15 será, e iso pode substituír as outras variables.
No peor dos casos, pode substituír algúns dos interna do seu programa
mecanismos de control, facendo que o seu programa para realmente executar instrucións diferentes
do que se pretendía.
>> Agora, non é común para facelo accidentalmente,
pero esta é unha técnica moi común que os bandidos usan para romper programas
e poñer o código malicioso en computadores de outros.
Polo tanto, non podemos só usar a nosa solución inxenua.
Necesitamos un xeito de evitar que os nosos programas de ser vulnerable
a un ataque de estourido de buffer.
Para facer isto, cómpre ter seguro de que o noso buffer pode crecer como lemos
máis a entrada do usuario.
A solución? Usamos un buffer alocado heap.
Desde que se redimensioná-lo usando o redimensionamento da función realloc,
e manter o control de dous números - o índice do próximo slot baleiro no buffer
é a lonxitude ou a capacidade da memoria intermedia.
Lemos en carácteres de usuario de cada vez usando a función fgetc.
O argumento da función fgetc leva - stdin - é unha referencia para a cadea de entrada estándar,
que é unha canle de entrada pre-conectado que é usado para transferir a entrada do usuario
a partir do terminal para o programa.
>> Sempre que o usuario escribe un novo personaxe, imos comprobar a ver se o índice
da rañura libre seguinte máis 1 é maior que a capacidade da memoria intermedia.
A unha, porque se trata o seguinte contido libre e 5,
entón lonxitude noso buffer debe ser de 6 a 0 grazas a indexación.
Se ficar sen espazo no buffer, entón intentamos redimensioná-la,
dobrando-o para que podemos reducir o número de veces que redimensionar
se o usuario está escribindo unha secuencia moi longa.
A secuencia quedou moito tempo ou se queda sen memoria heap,
que liberar noso buffer e nulos retorno.
>> Por fin, engada o char para o buffer.
Unha vez que os golpes de usuario entrar ou volver, sinalizando unha nova liña,
ou especial char - control d - o que indica un fin de insumos,
facemos unha comprobación para ver se o usuario realmente escribiu algo.
Se non, imos voltar nulo.
Se non, porque o noso tapón pode ser maior que necesitamos,
no peor dos casos, é case dúas veces maior do que necesitamos
sempre que dobrar cada vez que cambiar o tamaño,
que facer unha nova copia da cadea usando só a cantidade de espazo que precisamos.
Nós engadimos un 1 extra para a chamada malloc,
de modo que non hai espazo para o carácter especial terminador nulo - o \ 0,
que engadir a cadea, unha vez que copiar o resto dos personaxes,
utilizando, en vez de strncpy strcpy
de xeito que podemos especificar exactamente cantos caracteres que desexa copiar.
Strcpy copia ata acadar un \ 0.
Entón, liberar o noso buffer e devolver a copia para o chamador.
>> Quen diría que unha simple función de aparencia pode ser tan complicado?
Agora xa sabe o que vai á biblioteca CS50.
>> O meu nome é Nate Hardison, e este é o CS50.
[CS50.TV]