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 é uma ferramenta útil que temos instalado no aparelho 5 00:00:11,510 --> 00:00:15,870 para torná-lo mais fácil para você escrever programas que solicitam entrada dos usuários. 6 00:00:15,870 --> 00:00:21,670 Neste vídeo, vamos puxar a cortina e olhar para o que é exatamente na biblioteca CS50. 7 00:00:21,670 --> 00:00:25,520 >> No vídeo em bibliotecas C, falamos sobre como você # include arquivos cabeçalhos 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ão você ligar com um arquivo de biblioteca binário durante a fase de vinculação 10 00:00:31,150 --> 00:00:33,140 do processo de compilação. 11 00:00:33,140 --> 00:00:36,440 Os arquivos de cabeçalho especificar a interface da biblioteca. 12 00:00:36,440 --> 00:00:41,280 Isto é, eles detalhadamente todos os recursos que a biblioteca tem disponível para você usar, 13 00:00:41,280 --> 00:00:45,250 como declarações de funções, constantes e tipos de dados. 14 00:00:45,250 --> 00:00:48,890 O arquivo de biblioteca binário contém a implementação da biblioteca, 15 00:00:48,890 --> 00:00:54,580 que é compilado a partir de arquivos da biblioteca de cabeçalho e as da biblioteca. c arquivos de código fonte. 16 00:00:54,580 --> 00:00:59,820 >> O arquivo de biblioteca binário não é muito interessante para olhar já que é, bem, em binário. 17 00:00:59,820 --> 00:01:03,300 Então, vamos dar uma olhada nos arquivos de cabeçalho para a biblioteca vez. 18 00:01:03,300 --> 00:01:07,710 Neste caso, há apenas um arquivo de cabeçalho chamado cs50.h. 19 00:01:07,710 --> 00:01:11,040 Nós instalado no usuário incluir o diretório 20 00:01:11,040 --> 00:01:15,150 junto com os arquivos das bibliotecas do sistema de outros cabeçalho. 21 00:01:15,150 --> 00:01:21,530 >> Uma das primeiras coisas que você vai notar é que cs50.h # inclui arquivos de cabeçalho de outras bibliotecas - 22 00:01:21,530 --> 00:01:25,670 float, limites, padrão bool e lib padrão. 23 00:01:25,670 --> 00:01:28,800 Mais uma vez, seguindo o princípio de não reinventar a roda, 24 00:01:28,800 --> 00:01:33,490 nós construímos a biblioteca CS0 usando as ferramentas que outros fornecidos por nós. 25 00:01:33,490 --> 00:01:38,690 >> A próxima coisa que você vai ver na biblioteca é que nós definimos um novo tipo chamado "cadeia". 26 00:01:38,690 --> 00:01:42,330 Esta linha realmente apenas cria um alias para o tipo char *, 27 00:01:42,330 --> 00:01:46,000 para que ele não passe de mágica imbuir o novo tipo de grupo com atributos 28 00:01:46,000 --> 00:01:49,650 comumente associado com objetos de cadeia em outras línguas, 29 00:01:49,650 --> 00:01:50,850 tais como o comprimento. 30 00:01:50,850 --> 00:01:55,180 A razão por que fiz isso é para proteger novos programadores de os detalhes 31 00:01:55,180 --> 00:01:57,580 de ponteiros até que estejam prontos. 32 00:01:57,580 --> 00:02:00,130 >> A próxima parte do arquivo de cabeçalho é a declaração das funções 33 00:02:00,130 --> 00:02:04,410 que a biblioteca CS50 proporciona, juntamente com a documentação. 34 00:02:04,410 --> 00:02:06,940 Observe o nível de detalhe nos comentários aqui. 35 00:02:06,940 --> 00:02:10,560 Isso é super importante para que as pessoas sabem como usar essas funções. 36 00:02:10,560 --> 00:02:19,150 Nós declaramos, por sua vez, funciona para solicitar ao usuário e caracteres de retorno, duplas, carros alegóricos, ints, 37 00:02:19,150 --> 00:02:24,160 tempo anseia, e cordas, usando nosso próprio tipo string. 38 00:02:24,160 --> 00:02:26,260 Seguindo o princípio da ocultação de informações, 39 00:02:26,260 --> 00:02:31,640 temos que colocar a nossa definição em um arquivo de implementação separada c -. cs50.c - 40 00:02:31,640 --> 00:02:35,110 localizado no diretório de origem do usuário. 41 00:02:35,110 --> 00:02:38,040 Nós fornecemos o arquivo de modo que você pode dar uma olhada nisso, 42 00:02:38,040 --> 00:02:41,490 aprender com ele, e recompilar em máquinas diferentes, se desejar, 43 00:02:41,490 --> 00:02:45,510 mesmo que nós achamos que é melhor para trabalhar no aparelho para esta classe. 44 00:02:45,510 --> 00:02:47,580 De qualquer forma, vamos dar uma olhada nele agora. 45 00:02:49,020 --> 00:02:54,620 >> As funções getchar, GetDouble, GetFloat, GetInt, e GetLongLong 46 00:02:54,620 --> 00:02:58,160 são todos construídos no topo da função GetString. 47 00:02:58,160 --> 00:03:01,510 Acontece que todos eles seguem basicamente o mesmo padrão. 48 00:03:01,510 --> 00:03:04,870 Eles usam um loop while para solicitar ao usuário uma linha de entrada. 49 00:03:04,870 --> 00:03:08,430 Eles retornam um valor especial se o usuário insere uma linha vazia. 50 00:03:08,430 --> 00:03:11,750 Eles tentam analisar a entrada do usuário como o tipo apropriado, 51 00:03:11,750 --> 00:03:15,010 seja ele um char, uma dupla, um float, etc 52 00:03:15,010 --> 00:03:18,710 E então eles ou devolver o resultado se a entrada foi analisado com êxito 53 00:03:18,710 --> 00:03:21,330 ou eles Reprompt o utilizador. 54 00:03:21,330 --> 00:03:24,230 >> Em um nível alto, não há nada muito complicado aqui. 55 00:03:24,230 --> 00:03:28,760 Você pode ter escrito o código de estrutura semelhante a si mesmo no passado. 56 00:03:28,760 --> 00:03:34,720 Talvez a parte mais enigmática para o futuro é a chamada sscanf que analisa a entrada do usuário. 57 00:03:34,720 --> 00:03:38,160 Sscanf é parte da família de conversão de entrada formato. 58 00:03:38,160 --> 00:03:42,300 Vive em io.h padrão, e seu trabalho é analisar uma seqüência C, 59 00:03:42,300 --> 00:03:46,520 de acordo com um formato específico, armazenando os resultados de análise na variável 60 00:03:46,520 --> 00:03:48,720 fornecido pelo chamador. 61 00:03:48,720 --> 00:03:53,570 Desde a entrada de funções de conversão de formato são muito úteis, as funções amplamente utilizados 62 00:03:53,570 --> 00:03:56,160 que não são super intuitivo em 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 é um char * - um ponteiro para um personagem. 65 00:04:03,330 --> 00:04:05,150 Para a função de trabalhar corretamente, 66 00:04:05,150 --> 00:04:08,340 esse caráter deve ser o primeiro caractere de uma string C, 67 00:04:08,340 --> 00:04:12,270 terminou com o nulo \ 0 personagem. 68 00:04:12,270 --> 00:04:15,120 Esta é a string para analisar 69 00:04:15,120 --> 00:04:18,269 O segundo argumento para sscanf é uma seqüência de formato, 70 00:04:18,269 --> 00:04:20,839 tipicamente passado como uma constante string, 71 00:04:20,839 --> 00:04:24,040 e você pode ter visto uma seqüência como esta antes, quando usando printf. 72 00:04:24,040 --> 00:04:28,650 Um sinal de porcentagem no formato de string indica um especificador de conversão. 73 00:04:28,650 --> 00:04:30,850 O caractere imediatamente após um sinal de porcentagem, 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 Em GetInt, você vê que há uma d% e% c. 76 00:04:40,090 --> 00:04:48,690 Isto significa que sscanf vai tentar um int decimal - o% d - e um char - o c%. 77 00:04:48,690 --> 00:04:51,510 Para cada especificador de conversão na string de formato, 78 00:04:51,510 --> 00:04:56,620 sscanf espera um argumento correspondente mais tarde, em sua lista de argumentos. 79 00:04:56,620 --> 00:05:00,850 Esse argumento deve apontar para um local devidamente digitado 80 00:05:00,850 --> 00:05:04,000 em que para armazenar o resultado da conversão. 81 00:05:04,000 --> 00:05:08,910 >> A maneira típica de se fazer isso é criar uma variável na pilha antes da chamada sscanf 82 00:05:08,910 --> 00:05:11,440 para cada item que você deseja analisar a partir da cadeia 83 00:05:11,440 --> 00:05:15,520 e então usar o operador de endereço - o comercial - para passar ponteiros 84 00:05:15,520 --> 00:05:19,100 a essas variáveis ​​para a chamada sscanf. 85 00:05:19,100 --> 00:05:22,720 Você pode ver que em GetInt fazemos exatamente isso. 86 00:05:22,720 --> 00:05:28,240 Logo antes de a chamada sscanf, nós declaramos um int chamado n e um c chamada char na pilha, 87 00:05:28,240 --> 00:05:32,340 e passamos ponteiros para eles na chamada sscanf. 88 00:05:32,340 --> 00:05:35,800 Colocar essas variáveis ​​na pilha é preferível usar o espaço alocado 89 00:05:35,800 --> 00:05:39,350 no heap com malloc, uma vez que você evitar a sobrecarga da chamada malloc, 90 00:05:39,350 --> 00:05:43,060 e você não tem que se preocupar com vazamento de memória. 91 00:05:43,060 --> 00:05:47,280 Caracteres não prefixadas por um sinal de porcentagem não solicitam a conversão. 92 00:05:47,280 --> 00:05:50,380 Em vez disso, basta adicionar a especificação de formato. 93 00:05:50,380 --> 00:05:56,500 >> Por exemplo, se a seqüência de formato em GetInt fosse um d% em vez disso, 94 00:05:56,500 --> 00:05:59,800 sscanf iria procurar a letra seguido de um int, 95 00:05:59,800 --> 00:06:04,360 e enquanto ele tentaria converter o int, que não iria fazer mais nada com o um. 96 00:06:04,360 --> 00:06:07,440 A única exceção a isso é espaço em branco. 97 00:06:07,440 --> 00:06:11,030 Caracteres de espaço em branco na string formato de combinar com qualquer quantidade de espaços em branco - 98 00:06:11,030 --> 00:06:12,890 até mesmo nenhum. 99 00:06:12,890 --> 00:06:18,100 Então, é por isso que o comentário menciona possivelmente com os principais e / ou espaços em branco. 100 00:06:18,100 --> 00:06:22,910 Então, neste momento, parece que o nosso apelo sscanf tentará analisar a seqüência de entrada do usuário 101 00:06:22,910 --> 00:06:25,380 verificando possíveis espaços em branco, 102 00:06:25,380 --> 00:06:29,300 seguido de um int que irá ser convertida e armazenada na variável int n 103 00:06:29,300 --> 00:06:33,090 seguido por uma certa quantidade de espaço em branco, e seguido por um caractere 104 00:06:33,090 --> 00:06:35,810 armazenado na variável 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 irá analisar a linha de entrada do início ao fim, 107 00:06:41,560 --> 00:06:44,860 parando quando ele atinge a extremidade ou quando um caractere no input 108 00:06:44,860 --> 00:06:49,320 não corresponde um personagem formato ou quando ele não pode fazer uma conversão. 109 00:06:49,320 --> 00:06:52,690 Seu valor de retorno é usado para isolar quando parou. 110 00:06:52,690 --> 00:06:55,670 Se ele parou, porque chegou ao fim da cadeia de entrada 111 00:06:55,670 --> 00:07:00,630 antes de fazer as conversões e antes de deixar de coincidir com parte da cadeia de formato, 112 00:07:00,630 --> 00:07:04,840 em seguida, o EOF especial constante é retornado. 113 00:07:04,840 --> 00:07:08,200 Caso contrário, ele retorna o número de conversões bem-sucedidas, 114 00:07:08,200 --> 00:07:14,380 o que pode ser 0, 1 ou 2, uma vez que já pediu duas conversões. 115 00:07:14,380 --> 00:07:19,000 No nosso caso, queremos ter certeza de que o usuário digitou em um int e apenas um int. 116 00:07:19,000 --> 00:07:23,370 >> Então, nós queremos sscanf para retornar 1. Veja por quê? 117 00:07:23,370 --> 00:07:26,850 Se sscanf retornado 0, então não conversões foram feitas, 118 00:07:26,850 --> 00:07:31,690 de modo que o utilizador escreveu algo diferente de um int no início da entrada. 119 00:07:31,690 --> 00:07:37,100 Se sscanf retorna 2, então o utilizador não digita adequadamente no começo da entrada, 120 00:07:37,100 --> 00:07:41,390 mas então digitada em alguns caracteres não-espaço em branco depois 121 00:07:41,390 --> 00:07:44,940 uma vez que a% c conversão teve êxito. 122 00:07:44,940 --> 00:07:49,570 Uau, isso é uma longa explicação para uma chamada de função. 123 00:07:49,570 --> 00:07:53,460 Enfim, se você quiser mais informações sobre sscanf e seus irmãos, 124 00:07:53,460 --> 00:07:57,130 confira as páginas do manual, o Google, ou ambos. 125 00:07:57,130 --> 00:07:58,780 Há muitas opções de string de formato, 126 00:07:58,780 --> 00:08:03,830 e estes podem poupar-lhe muito do trabalho manual ao tentar analisar seqüências em C. 127 00:08:03,830 --> 00:08:07,180 >> A última função na biblioteca é olhar para GetString. 128 00:08:07,180 --> 00:08:10,310 Acontece que GetString é uma função difícil de escrever corretamente, 129 00:08:10,310 --> 00:08:14,290 embora parece que tal uma tarefa simples, comum. 130 00:08:14,290 --> 00:08:16,170 Por que é este o caso? 131 00:08:16,170 --> 00:08:21,380 Bem, vamos pensar sobre como estamos indo para armazenar a linha que o usuário digita pol 132 00:08:21,380 --> 00:08:23,880 Uma vez que uma string é uma seqüência de caracteres, 133 00:08:23,880 --> 00:08:26,430 podemos querer armazená-lo em uma matriz na pilha, 134 00:08:26,430 --> 00:08:31,250 mas seria preciso saber quanto tempo a matriz vai ser quando declara. 135 00:08:31,250 --> 00:08:34,030 Da mesma forma, se queremos colocá-lo na pilha, 136 00:08:34,030 --> 00:08:38,090 precisamos passar para malloc o número de bytes que queremos reserva, 137 00:08:38,090 --> 00:08:39,730 mas isto é impossível. 138 00:08:39,730 --> 00:08:42,760 Nós não temos idéia de quantos caracteres o usuário digitar 139 00:08:42,760 --> 00:08:46,590 antes que o usuário não digita. 140 00:08:46,590 --> 00:08:50,720 >> Uma solução ingênua para este problema é só reservar um pedaço grande de espaço, por exemplo, 141 00:08:50,720 --> 00:08:54,540 um bloco de 1000 caracteres para a entrada do utilizador, 142 00:08:54,540 --> 00:08:57,980 assumindo que o usuário nunca deveria digitar uma seqüência de tanto tempo. 143 00:08:57,980 --> 00:09:00,810 Esta é uma idéia ruim por dois motivos. 144 00:09:00,810 --> 00:09:05,280 Primeiro, assumindo que os usuários normalmente não digitar em cordas muito tempo, 145 00:09:05,280 --> 00:09:07,610 você poderia desperdiçar uma grande quantidade de memória. 146 00:09:07,610 --> 00:09:10,530 Em máquinas modernas, isso pode não ser um problema se você fizer isso 147 00:09:10,530 --> 00:09:13,890 em um ou dois casos isolados, 148 00:09:13,890 --> 00:09:17,630 mas se você está tomando a entrada do usuário em um loop e armazenamento para uso posterior, 149 00:09:17,630 --> 00:09:20,870 você pode rapidamente sugar até uma tonelada de memória. 150 00:09:20,870 --> 00:09:24,450 Além disso, se o programa que você está escrevendo é para um computador menor - 151 00:09:24,450 --> 00:09:28,100 um dispositivo como um smartphone ou qualquer outra coisa com memória limitada - 152 00:09:28,100 --> 00:09:32,060 esta solução irá causar problemas muito mais rápido. 153 00:09:32,060 --> 00:09:36,450 O motivo, segundo mais grave não fazer isso é que ele deixa o seu programa vulnerável 154 00:09:36,450 --> 00:09:39,710 para o que é chamado um ataque de estouro de buffer. 155 00:09:39,710 --> 00:09:45,840 Na programação, um buffer de memória é usado para armazenar temporariamente os dados de entrada ou de saída, 156 00:09:45,840 --> 00:09:48,980 que neste caso é o nosso bloco 1000-char. 157 00:09:48,980 --> 00:09:53,370 Um buffer overflow ocorre quando os dados são gravados após o fim do bloco. 158 00:09:53,370 --> 00:09:57,790 >> Por exemplo, se um utilizador faz realmente tipo em mais de 1000 caracteres. 159 00:09:57,790 --> 00:10:01,570 Você pode ter experimentado esta acidentalmente durante a programação com matrizes. 160 00:10:01,570 --> 00:10:05,620 Se você tem uma matriz de 10 ints, nada impede você de tentar ler ou escrever 161 00:10:05,620 --> 00:10:07,810 o int 15. 162 00:10:07,810 --> 00:10:10,000 Não há avisos do compilador ou erros. 163 00:10:10,000 --> 00:10:13,250 O programa apenas erros para frente e acessa a memória 164 00:10:13,250 --> 00:10:18,150 onde ele acha o int 15 será, e isso pode substituir as outras variáveis. 165 00:10:18,150 --> 00:10:22,040 Na pior das hipóteses, você pode substituir alguns dos interna do seu programa 166 00:10:22,040 --> 00:10:26,820 mecanismos de controle, fazendo com que o seu programa para realmente executar instruções diferentes 167 00:10:26,820 --> 00:10:28,340 do que se pretendia. 168 00:10:28,340 --> 00:10:31,360 >> Agora, não é comum de fazer isso acidentalmente, 169 00:10:31,360 --> 00:10:35,150 mas esta é uma técnica bastante comum que os bandidos usam para quebrar programas 170 00:10:35,150 --> 00:10:39,080 e colocar o código malicioso em computadores de outras pessoas. 171 00:10:39,080 --> 00:10:42,910 Portanto, não podemos apenas usar nossa solução ingênua. 172 00:10:42,910 --> 00:10:45,590 Precisamos de uma maneira de evitar que os nossos programas de ser vulnerável 173 00:10:45,590 --> 00:10:47,880 a um ataque de estouro de buffer. 174 00:10:47,880 --> 00:10:51,430 Para fazer isso, precisamos ter certeza de que o nosso buffer pode crescer como lemos 175 00:10:51,430 --> 00:10:53,850 mais a entrada do usuário. 176 00:10:53,850 --> 00:10:57,440 A solução? Nós usamos um buffer alocado heap. 177 00:10:57,440 --> 00:10:59,950 Desde que pode redimensioná-lo usando o redimensionamento da função realloc, 178 00:10:59,950 --> 00:11:04,580 e manter o controle de dois números - o índice do próximo slot vazio no buffer 179 00:11:04,580 --> 00:11:08,390 e o comprimento ou a capacidade da memória intermédia. 180 00:11:08,390 --> 00:11:13,210 Lemos em caracteres do usuário de cada vez usando a função fgetc. 181 00:11:13,210 --> 00:11:19,360 O argumento da função fgetc leva - stdin - é uma referência para a cadeia de entrada padrão, 182 00:11:19,360 --> 00:11:23,810 que é um canal de entrada pré-ligado que é usado para transferir a entrada do utilizador 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 usuário digita em um novo personagem, vamos verificar para ver se o índice 185 00:11:29,890 --> 00:11:35,810 da ranhura livre seguinte mais 1 é maior do que a capacidade da memória intermédia. 186 00:11:35,810 --> 00:11:39,690 A uma, porque se trata de o próximo índice livre é 5, 187 00:11:39,690 --> 00:11:44,150 então comprimento nosso buffer deve ser de 6 a 0 graças a indexação. 188 00:11:44,150 --> 00:11:48,350 Se nós ficar sem espaço no buffer, então tentamos redimensioná-la, 189 00:11:48,350 --> 00:11:51,690 dobrando-o para que podemos reduzir o número de vezes que redimensionar 190 00:11:51,690 --> 00:11:54,760 se o usuário estiver digitando uma seqüência muito longa. 191 00:11:54,760 --> 00:11:57,950 Se a seqüência ficou muito tempo ou se ficar sem memória heap, 192 00:11:57,950 --> 00:12:01,350 que libertar nosso buffer e nulos retorno. 193 00:12:01,350 --> 00:12:04,170 >> Por fim, acrescente o char para o buffer. 194 00:12:04,170 --> 00:12:08,200 Uma vez que os golpes de usuário entrar ou retornar, sinalizando uma nova linha, 195 00:12:08,200 --> 00:12:12,050 ou o especial char - controle d - o que sinaliza um fim de insumos, 196 00:12:12,050 --> 00:12:16,240 fazemos uma verificação para ver se o usuário realmente digitou alguma coisa. 197 00:12:16,240 --> 00:12:18,820 Se não, vamos retornar nulo. 198 00:12:18,820 --> 00:12:22,280 Caso contrário, porque o nosso tampão é provavelmente maior do que precisamos, 199 00:12:22,280 --> 00:12:24,830 na pior das hipóteses, é quase duas vezes maior do que precisamos 200 00:12:24,830 --> 00:12:27,830 desde que dobrar a cada vez que redimensionar, 201 00:12:27,830 --> 00:12:31,840 que fazer uma nova cópia da string usando apenas a quantidade de espaço que precisamos. 202 00:12:31,840 --> 00:12:34,220 Nós adicionamos um 1 extra para a chamada malloc, 203 00:12:34,220 --> 00:12:37,810 de modo que não há espaço para o caractere especial terminador nulo - o \ 0, 204 00:12:37,810 --> 00:12:41,990 que acrescentar para a cadeia, uma vez que copiar o resto dos personagens, 205 00:12:41,990 --> 00:12:45,060 utilizando, em vez de strncpy strcpy 206 00:12:45,060 --> 00:12:48,830 de modo que podemos especificar exatamente quantos caracteres que deseja copiar. 207 00:12:48,830 --> 00:12:51,690 Strcpy copia até atingir um \ 0. 208 00:12:51,690 --> 00:12:55,740 Então, libertar o nosso buffer e devolver a cópia para o chamador. 209 00:12:55,740 --> 00:12:59,840 >> Quem diria que uma simples função de aparência pode ser tão complicado? 210 00:12:59,840 --> 00:13:02,820 Agora você já sabe o que vai para a biblioteca CS50. 211 00:13:02,820 --> 00:13:06,470 >> Meu nome é Nate Hardison, e este é o CS50. 212 00:13:06,470 --> 00:13:08,350 [CS50.TV]