[Powered by Google Translate] [Valgrind] [Nate Hardison, Harvard University] Este é CS50, CS50.TV] Alguns dos erros mais difíceis de programas em C vêm da má gestão da memória. Há um número enorme de maneiras de estragar as coisas, incluindo alocar a quantidade errada de memória, esquecendo-se de inicializar variáveis, escrito antes ou após o final de um tampão, e liberando manter memória várias vezes. Os sintomas variam de falhas intermitentes para valores misteriosamente substituído, muitas vezes em locais e horários muito distantes do erro original. Detectar o problema observado de volta para a causa subjacente pode ser um desafio, mas, felizmente, existe um programa útil chamado Valgrind que podem fazer muita coisa para ajudar. Você executar um programa no Valgrind para permitir verificação extensa de alocações de heap de memória e acessos. Quando Valgrind detectar um problema, dá-lhe imediato, informação directa que lhe permite mais facilmente encontrar e corrigir o problema. Valgrind também relatórios sobre problemas de memória menos mortais, tais como vazamentos de memória, alocação de memória heap, e esquecer de libertá-la. Como nosso compilador Clang, em nosso depurador, GDB, Valgrind é software livre, e é instalado no aparelho. Valgrind é executado em seu executável, não o seu c. ou h. arquivos de código fonte, por isso não deixe de ter compilado uma cópia atualizada do seu programa usando Clang ou fazer. Em seguida, executando o seu programa sob Valgrind pode ser tão simples como prefixar o comando do programa com o padrão Valgrind palavra, que inicia Valgrind e executa o programa dentro dele. Ao iniciar, Valgrind faz algum complexo jiggering para configurar o executável para as verificações de memória, por isso pode demorar um pouco para se levantar e correr. O programa irá então executar normalmente, seja muito mais lentamente, e quando termina, Valgrind irá imprimir um resumo de seu uso de memória. Se tudo correr bem, ele será parecido com este: Neste caso,. / Clean_program é o caminho para o programa que eu quero correr. E enquanto este não tomar quaisquer argumentos, se isso acontecesse eu apenas tack-los para o final do comando, como de costume. Programa limpa é apenas um programa um pouco bobo eu criei que aloca espaço para um bloco de inteiros na pilha, colocar alguns valores dentro deles, e libera o bloco inteiro. Isto é o que você está atirando para, sem erros e sem vazamentos. Outra métrica importante é o número total de bytes atribuídos. Dependendo do programa, se suas atribuições estão nas megabytes ou mais, provavelmente você está fazendo algo errado. Você está desnecessariamente armazenar duplicatas? Você está usando a pilha para armazenamento, quando seria melhor usar a pilha? Assim, erros de memória podem ser verdadeiramente mal. Os mais evidentes causar acidentes espetaculares, mas, mesmo assim, ele ainda pode ser difícil identificar exatamente o que levou ao acidente. Mais insidiosamente, um programa com um erro de memória ainda pode compilar limpa e pode ainda parecem funcionar corretamente porque você conseguiu ter sorte na maioria das vezes. Depois de vários "bons resultados", você pode apenas pensar que um acidente é um golpe de sorte do computador, mas o computador nunca está errado. Correndo Valgrind pode ajudá-lo a rastrear a causa de erros de memória visíveis bem como encontrar espreita erros você nem sabe ainda sobre. Cada vez Valgrind detectar um problema, ele imprime informações sobre o que observaram. Cada item é bastante concisa - a linha da fonte da instrução ofender, qual é o problema, Informações e um pouco sobre a memória envolvida - mas muitas vezes é informação suficiente para direcionar sua atenção para o lugar certo. Aqui é um exemplo de execução no Valgrind um programa de buggy que faz uma leitura inválido de memória heap. Nós vemos nenhum erro ou aviso na compilação. Uh-oh, o resumo de erro diz que há dois erros - duas leituras inválido de tamanho 4 - bytes, que é. Tanto maus lê ocorreu na função principal de invalid_read.c, o primeiro na linha 16 e o ​​segundo na linha 19. Vamos olhar para o código. Parece que a primeira chamada para printf tenta ler um int passado o fim do nosso bloco de memória. Se olharmos para trás na saída do Valgrind, vemos que Valgrind nos disse exatamente isso. O endereço que estamos tentando ler começa 0 bytes após o fim do bloco de 16 bytes de tamanho - quatro de 32 bits ints que alocados. Ou seja, o endereço que estávamos tentando ler começa logo no final do nosso bloco, assim como vemos em nosso chamado mau printf. Agora, inválida leituras pode não parecer tão grande de um negócio, mas se você está usando esses dados para controlar o fluxo de seu programa - por exemplo, como parte de uma instrução if ou loop - então as coisas podem ir mal em silêncio. Veja como eu pode executar o programa invalid_read e nada fora do comum acontece. Assustador, não? Agora, vamos olhar mais alguns tipos de erros que você pode encontrar em seu código, e vamos ver como Valgrind detecta. Nós só vimos um exemplo de um invalid_read, então agora vamos verificar um invalid_write. Mais uma vez, nenhum erro ou aviso na compilação. Ok, Valgrind diz que há dois erros neste programa - e invalid_write e um invalid_read. Vamos verificar este código. Parece que temos uma instância do strlen clássico mais um bug. O código não malloc um byte extra de espaço para o personagem / 0, por isso, quando str cópia foi para escrevê-lo em ssubstrlen "CS50 rocks!" escreveu um byte após o final do nosso bloco. O invalid_read vem quando nós fazemos a nossa chamada para printf. Printf acaba de ler memória inválido, quando ele lê o / 0 caráter como ele olha para o final deste corda E é impressão. Mas nada disso escapou Valgrind. Vemos que chamou a invalid_write como parte da cópia de str na linha 11 do principal, eo invalid_read é parte do printf. Rock on, Valgrind. Novamente, isso pode não parecer um grande negócio. Podemos executar este programa mais e mais fora de Valgrind e não ver nenhum sintoma de erro. No entanto, vamos olhar para uma pequena variação dessa para ver como as coisas podem ficar muito ruim. Assim, concedeu, estamos abusando de coisas mais do que apenas um pouco neste código. Estamos apenas a alocação de espaço na pilha para duas cordas o comprimento de CS50 rochas, desta vez, lembrando o / 0 personagem. Mas, então, jogar em uma seqüência super-longa no bloco de memória S que está apontando. Qual será o efeito que tem sobre o bloco de memória que aponta para T? Bem, se os pontos de T a memória que é apenas ao lado S, vindo logo depois, então nós poderia ter escrito sobre parte de T. Vamos executar esse código. Olhe para o que aconteceu. As cordas que armazenados em nossos blocos de heap ambos pareciam ter impressos corretamente. Nada parece errado em tudo. No entanto, vamos voltar para o nosso código e comente a linha onde copiar CS50 rochas no bloco de memória em segundo lugar, apontada por t. Agora, quando executar este código que deve só ver o conteúdo do primeiro bloco de memória imprimir. Whoa, mesmo que não fez cópia str quaisquer caracteres no bloco heap segundo, aquele apontado por T, temos uma impressão. De fato, a seqüência de nós recheado em nosso primeiro bloco invadiram o primeiro bloco e para dentro do segundo bloco, fazendo com que tudo pareça normal. Valgrind, porém, diz-nos a história verdadeira. Lá vamos nós. Todos aqueles inválido lê e escreve. Vejamos um exemplo de outro tipo de erro. Aqui fazemos algo bastante infeliz. Nós agarrar espaço para um int na pilha, e inicializar um ponteiro int - p - para apontar para esse espaço. No entanto, enquanto o nosso ponteiro é inicializado, os dados que ele está apontando para o que acaba de lixo é em que parte da pilha. Então, quando temos de carregar esses dados em int i, que tecnicamente inicializar i, mas fazê-lo com dados de lixo. A chamada para afirmar, que é uma macro de depuração útil definido no apropriadamente chamado biblioteca afirmar, irá abortar o programa se a sua condição de teste falhar. Isto é, se eu não for 0. Dependendo do que foi no espaço de pilha, apontado por p, este programa pode funcionar algumas vezes e não em outros momentos. Se funcionar, nós estamos apenas começando sorte. O compilador não vai pegar esse erro, mas Valgrind vontade certeza. Há que ver o erro decorrente de nosso uso desses dados sucata. Quando você aloca memória heap, mas não desalocar-lo ou libertá-lo, que é chamado de uma fuga. Para um pequeno programa de curta duração que corre e sai imediatamente, vazamentos são bastante inofensivos, mas para um projeto de maior porte e / ou longevidade, até mesmo um pequeno vazamento pode agravar em algo maior. Para CS50, nós esperamos que você cuidar de libertar toda a memória heap que você alocar, desde que nós queremos que você construa as habilidades para lidar adequadamente com o processo manual exigida pela C. Para isso, o programa deve ter uma exata um-para-um entre malloc e chamadas gratuitas. Felizmente, Valgrind pode ajudá-lo com vazamentos de memória também. Aqui está um programa furado chamado leak.c que aloca espaço na pilha, escreve para ele, mas não libertá-lo. Nós compilar com Fazer e executá-lo em Valgrind, e vemos que, enquanto temos nenhum erro de memória, temos um vazamento. Há 16 bytes definitivamente perdidos, o que significa que o ponteiro para que a memória não estava no escopo quando o programa foi encerrado. Agora, Valgrind não nos dá uma tonelada de informações sobre o vazamento, mas se seguirmos essa pequena nota que ele dá em direcção ao fundo do seu relatório executar novamente com - leak-check = total para ver os detalhes completos de memória vazada, vamos obter mais informações. Agora, no resumo heap, Valgrind nos diz onde a memória que se perdeu foi inicialmente atribuído. Assim como sabemos de olhar no código fonte, Valgrind nos informa que vazou a memória alocada com uma chamada a malloc na linha 8 da leak.c na função principal. Muito bacana. Valgrind categoriza vazamentos usando estes termos: Definitivamente perdido - isto é memória alocada pilha para que o programa não tem um ponteiro. Valgrind sabe que você já teve o ponteiro, mas, desde então, perdeu a noção do mesmo. Esta memória é definitivamente vazou. Indiretamente perdido - isto é memória alocada pilha a que apenas os apontadores para isso também são perdidas. Por exemplo, se você perdeu o ponteiro para o primeiro nó de uma lista ligada, em seguida, o primeiro nó si seria definitivamente perdidas, enquanto todos os nós subseqüentes seriam indiretamente perdido. Possivelmente perdeu - esta é a memória alocada pilha ao qual Valgrind não pode ter a certeza se existe um ponteiro ou não. Ainda acessível é a memória alocada pilha em que o programa tem ainda um ponteiro na saída, o que normalmente significa que uma variável global pontos para ele. Para verificar esses vazamentos, você também tem que incluir a opção - Ainda alcançável = yes na sua invocação de Valgrind. Estes casos podem requerer diferentes estratégias diferentes para limpá-las para cima, mas vazamentos devem ser eliminados. Infelizmente, consertar vazamentos pode ser difícil de fazer, já que as chamadas incorretas a livre pode explodir o seu programa. Por exemplo, se olharmos para invalid_free.c, vemos um exemplo de desalocação de memória ruim. O que deveria ser uma única chamada para liberar todo o bloco de memória apontada por int_block, em vez disso se tornar uma tentativa de libertar cada seção int porte da memória individualmente. Isto irá falhar catastroficamente. Boom! O que um erro. Isto definitivamente não é bom. Se você está preso com esse tipo de erro, embora, e você não sabe para onde olhar, cair para trás em seu novo melhor amigo. Você adivinhou - Valgrind. Valgrind, como sempre, sabe exatamente o que está acontecendo. As contagens alloc e livre não combinam. Temos um alloc e 4 liberta. E Valgrind também nos diz que a chamada má primeira livre - o que desencadeou a blowup - está vindo - linha 16. Como você pode ver, as chamadas para libertar maus são muito ruins, por isso recomendamos deixar o seu programa de vazamento enquanto você está trabalhando para conseguir a funcionalidade correta. Comece procurando vazamentos somente após o programa está funcionando corretamente, sem quaisquer outros erros. E isso é tudo o que temos para este vídeo. Agora, o que você está esperando? Ir executar Valgrind em seus programas agora. Meu nome é Nate Hardison. Este é CS50. [CS50.TV]