[Powered by Google Translate] [CS50 Biblioteca]
[Nate Hardison] [Harvard University]
[Esta é CS50. CS50.TV]
A biblioteca CS50 é uma ferramenta útil que temos instalado no aparelho
para torná-lo mais fácil para você escrever programas que solicitam entrada dos usuários.
Neste vídeo, vamos puxar a cortina e olhar para o que é exatamente na biblioteca CS50.
>> No vídeo em bibliotecas C, falamos sobre como você # include arquivos cabeçalhos
da biblioteca no seu código fonte,
e então você ligar com um arquivo de biblioteca binário durante a fase de vinculação
do processo de compilação.
Os arquivos de cabeçalho especificar a interface da biblioteca.
Isto é, eles detalhadamente todos os recursos que a biblioteca tem disponível para você usar,
como declarações de funções, constantes e tipos de dados.
O arquivo de biblioteca binário contém a implementação da biblioteca,
que é compilado a partir de arquivos da biblioteca de cabeçalho e as da biblioteca. c arquivos de código fonte.
>> O arquivo de biblioteca binário não é muito interessante para olhar já que é, bem, em binário.
Então, vamos dar uma olhada nos arquivos de cabeçalho para a biblioteca vez.
Neste caso, há apenas um arquivo de cabeçalho chamado cs50.h.
Nós instalado no usuário incluir o diretório
junto com os arquivos das bibliotecas do sistema de outros cabeçalho.
>> Uma das primeiras coisas que você vai notar é que cs50.h # inclui arquivos de cabeçalho de outras bibliotecas -
float, limites, padrão bool e lib padrão.
Mais uma vez, seguindo o princípio de não reinventar a roda,
nós construímos a biblioteca CS0 usando as ferramentas que outros fornecidos por nós.
>> A próxima coisa que você vai ver na biblioteca é que nós definimos um novo tipo chamado "cadeia".
Esta linha realmente apenas cria um alias para o tipo char *,
para que ele não passe de mágica imbuir o novo tipo de grupo com atributos
comumente associado com objetos de cadeia em outras línguas,
tais como o comprimento.
A razão por que fiz isso é para proteger novos programadores de os detalhes
de ponteiros até que estejam prontos.
>> A próxima parte do arquivo de cabeçalho é a declaração das funções
que a biblioteca CS50 proporciona, juntamente com a documentação.
Observe o nível de detalhe nos comentários aqui.
Isso é super importante para que as pessoas sabem como usar essas funções.
Nós declaramos, por sua vez, funciona para solicitar ao usuário e caracteres de retorno, duplas, carros alegóricos, ints,
tempo anseia, e cordas, usando nosso próprio tipo string.
Seguindo o princípio da ocultação de informações,
temos que colocar a nossa definição em um arquivo de implementação separada c -. cs50.c -
localizado no diretório de origem do usuário.
Nós fornecemos o arquivo de modo que você pode dar uma olhada nisso,
aprender com ele, e recompilar em máquinas diferentes, se desejar,
mesmo que nós achamos que é melhor para trabalhar no aparelho para esta classe.
De qualquer forma, vamos dar uma olhada nele agora.
>> As funções getchar, GetDouble, GetFloat, GetInt, e GetLongLong
são todos construídos no topo da função GetString.
Acontece que todos eles seguem basicamente o mesmo padrão.
Eles usam um loop while para solicitar ao usuário uma linha de entrada.
Eles retornam um valor especial se o usuário insere uma linha vazia.
Eles tentam analisar a entrada do usuário como o tipo apropriado,
seja ele um char, uma dupla, um float, etc
E então eles ou devolver o resultado se a entrada foi analisado com êxito
ou eles Reprompt o utilizador.
>> Em um nível alto, não há nada muito complicado aqui.
Você pode ter escrito o código de estrutura semelhante a si mesmo no passado.
Talvez a parte mais enigmática para o futuro é a chamada sscanf que analisa a entrada do usuário.
Sscanf é parte da família de conversão de entrada formato.
Vive em io.h padrão, e seu trabalho é analisar uma seqüência C,
de acordo com um formato específico, armazenando os resultados de análise na variável
fornecido pelo chamador.
Desde a entrada de funções de conversão de formato são muito úteis, as funções amplamente utilizados
que não são super intuitivo em primeiro lugar,
nós falaremos sobre como sscanf funciona.
>> O primeiro argumento para sscanf é um char * - um ponteiro para um personagem.
Para a função de trabalhar corretamente,
esse caráter deve ser o primeiro caractere de uma string C,
terminou com o nulo \ 0 personagem.
Esta é a string para analisar
O segundo argumento para sscanf é uma seqüência de formato,
tipicamente passado como uma constante string,
e você pode ter visto uma seqüência como esta antes, quando usando printf.
Um sinal de porcentagem no formato de string indica um especificador de conversão.
O caractere imediatamente após um sinal de porcentagem,
indica o tipo C que queremos sscanf para converter.
Em GetInt, você vê que há uma d% e% c.
Isto significa que sscanf vai tentar um int decimal - o% d - e um char - o c%.
Para cada especificador de conversão na string de formato,
sscanf espera um argumento correspondente mais tarde, em sua lista de argumentos.
Esse argumento deve apontar para um local devidamente digitado
em que para armazenar o resultado da conversão.
>> A maneira típica de se fazer isso é criar uma variável na pilha antes da chamada sscanf
para cada item que você deseja analisar a partir da cadeia
e então usar o operador de endereço - o comercial - para passar ponteiros
a essas variáveis ​​para a chamada sscanf.
Você pode ver que em GetInt fazemos exatamente isso.
Logo antes de a chamada sscanf, nós declaramos um int chamado n e um c chamada char na pilha,
e passamos ponteiros para eles na chamada sscanf.
Colocar essas variáveis ​​na pilha é preferível usar o espaço alocado
no heap com malloc, uma vez que você evitar a sobrecarga da chamada malloc,
e você não tem que se preocupar com vazamento de memória.
Caracteres não prefixadas por um sinal de porcentagem não solicitam a conversão.
Em vez disso, basta adicionar a especificação de formato.
>> Por exemplo, se a seqüência de formato em GetInt fosse um d% em vez disso,
sscanf iria procurar a letra seguido de um int,
e enquanto ele tentaria converter o int, que não iria fazer mais nada com o um.
A única exceção a isso é espaço em branco.
Caracteres de espaço em branco na string formato de combinar com qualquer quantidade de espaços em branco -
até mesmo nenhum.
Então, é por isso que o comentário menciona possivelmente com os principais e / ou espaços em branco.
Então, neste momento, parece que o nosso apelo sscanf tentará analisar a seqüência de entrada do usuário
verificando possíveis espaços em branco,
seguido de um int que irá ser convertida e armazenada na variável int n
seguido por uma certa quantidade de espaço em branco, e seguido por um caractere
armazenado na variável char c.
>> E sobre o valor de retorno?
Sscanf irá analisar a linha de entrada do início ao fim,
parando quando ele atinge a extremidade ou quando um caractere no input
não corresponde um personagem formato ou quando ele não pode fazer uma conversão.
Seu valor de retorno é usado para isolar quando parou.
Se ele parou, porque chegou ao fim da cadeia de entrada
antes de fazer as conversões e antes de deixar de coincidir com parte da cadeia de formato,
em seguida, o EOF especial constante é retornado.
Caso contrário, ele retorna o número de conversões bem-sucedidas,
o que pode ser 0, 1 ou 2, uma vez que já pediu duas conversões.
No nosso caso, queremos ter certeza de que o usuário digitou em um int e apenas um int.
>> Então, nós queremos sscanf para retornar 1. Veja por quê?
Se sscanf retornado 0, então não conversões foram feitas,
de modo que o utilizador escreveu algo diferente de um int no início da entrada.
Se sscanf retorna 2, então o utilizador não digita adequadamente no começo da entrada,
mas então digitada em alguns caracteres não-espaço em branco depois
uma vez que a% c conversão teve êxito.
Uau, isso é uma longa explicação para uma chamada de função.
Enfim, se você quiser mais informações sobre sscanf e seus irmãos,
confira as páginas do manual, o Google, ou ambos.
Há muitas opções de string de formato,
e estes podem poupar-lhe muito do trabalho manual ao tentar analisar seqüências em C.
>> A última função na biblioteca é olhar para GetString.
Acontece que GetString é uma função difícil de escrever corretamente,
embora parece que tal uma tarefa simples, comum.
Por que é este o caso?
Bem, vamos pensar sobre como estamos indo para armazenar a linha que o usuário digita pol
Uma vez que uma string é uma seqüência de caracteres,
podemos querer armazená-lo em uma matriz na pilha,
mas seria preciso saber quanto tempo a matriz vai ser quando declara.
Da mesma forma, se queremos colocá-lo na pilha,
precisamos passar para malloc o número de bytes que queremos reserva,
mas isto é impossível.
Nós não temos idéia de quantos caracteres o usuário digitar
antes que o usuário não digita.
>> Uma solução ingênua para este problema é só reservar um pedaço grande de espaço, por exemplo,
um bloco de 1000 caracteres para a entrada do utilizador,
assumindo que o usuário nunca deveria digitar uma seqüência de tanto tempo.
Esta é uma idéia ruim por dois motivos.
Primeiro, assumindo que os usuários normalmente não digitar em cordas muito tempo,
você poderia desperdiçar uma grande quantidade de memória.
Em máquinas modernas, isso pode não ser um problema se você fizer isso
em um ou dois casos isolados,
mas se você está tomando a entrada do usuário em um loop e armazenamento para uso posterior,
você pode rapidamente sugar até uma tonelada de memória.
Além disso, se o programa que você está escrevendo é para um computador menor -
um dispositivo como um smartphone ou qualquer outra coisa com memória limitada -
esta solução irá causar problemas muito mais rápido.
O motivo, segundo mais grave não fazer isso é que ele deixa o seu programa vulnerável
para o que é chamado um ataque de estouro de buffer.
Na programação, um buffer de memória é usado para armazenar temporariamente os dados de entrada ou de saída,
que neste caso é o nosso bloco 1000-char.
Um buffer overflow ocorre quando os dados são gravados após o fim do bloco.
>> Por exemplo, se um utilizador faz realmente tipo em mais de 1000 caracteres.
Você pode ter experimentado esta acidentalmente durante a programação com matrizes.
Se você tem uma matriz de 10 ints, nada impede você de tentar ler ou escrever
o int 15.
Não há avisos do compilador ou erros.
O programa apenas erros para frente e acessa a memória
onde ele acha o int 15 será, e isso pode substituir as outras variáveis.
Na pior das hipóteses, você pode substituir alguns dos interna do seu programa
mecanismos de controle, fazendo com que o seu programa para realmente executar instruções diferentes
do que se pretendia.
>> Agora, não é comum de fazer isso acidentalmente,
mas esta é uma técnica bastante comum que os bandidos usam para quebrar programas
e colocar o código malicioso em computadores de outras pessoas.
Portanto, não podemos apenas usar nossa solução ingênua.
Precisamos de uma maneira de evitar que os nossos programas de ser vulnerável
a um ataque de estouro de buffer.
Para fazer isso, precisamos ter certeza de que o nosso buffer pode crescer como lemos
mais a entrada do usuário.
A solução? Nós usamos um buffer alocado heap.
Desde que pode redimensioná-lo usando o redimensionamento da função realloc,
e manter o controle de dois números - o índice do próximo slot vazio no buffer
e o comprimento ou a capacidade da memória intermédia.
Lemos em caracteres do usuário de cada vez usando a função fgetc.
O argumento da função fgetc leva - stdin - é uma referência para a cadeia de entrada padrão,
que é um canal de entrada pré-ligado que é usado para transferir a entrada do utilizador
a partir do terminal para o programa.
>> Sempre que o usuário digita em um novo personagem, vamos verificar para ver se o índice
da ranhura livre seguinte mais 1 é maior do que a capacidade da memória intermédia.
A uma, porque se trata de o próximo índice livre é 5,
então comprimento nosso buffer deve ser de 6 a 0 graças a indexação.
Se nós ficar sem espaço no buffer, então tentamos redimensioná-la,
dobrando-o para que podemos reduzir o número de vezes que redimensionar
se o usuário estiver digitando uma seqüência muito longa.
Se a seqüência ficou muito tempo ou se ficar sem memória heap,
que libertar nosso buffer e nulos retorno.
>> Por fim, acrescente o char para o buffer.
Uma vez que os golpes de usuário entrar ou retornar, sinalizando uma nova linha,
ou o especial char - controle d - o que sinaliza um fim de insumos,
fazemos uma verificação para ver se o usuário realmente digitou alguma coisa.
Se não, vamos retornar nulo.
Caso contrário, porque o nosso tampão é provavelmente maior do que precisamos,
na pior das hipóteses, é quase duas vezes maior do que precisamos
desde que dobrar a cada vez que redimensionar,
que fazer uma nova cópia da string usando apenas a quantidade de espaço que precisamos.
Nós adicionamos um 1 extra para a chamada malloc,
de modo que não há espaço para o caractere especial terminador nulo - o \ 0,
que acrescentar para a cadeia, uma vez que copiar o resto dos personagens,
utilizando, em vez de strncpy strcpy
de modo que podemos especificar exatamente quantos caracteres que deseja copiar.
Strcpy copia até atingir um \ 0.
Então, libertar o nosso buffer e devolver a cópia para o chamador.
>> Quem diria que uma simples função de aparência pode ser tão complicado?
Agora você já sabe o que vai para a biblioteca CS50.
>> Meu nome é Nate Hardison, e este é o CS50.
[CS50.TV]