[? DAN ARMADARAS:?] Hi, Eu sou [? Dan Armadaras?]. Hoje, nós estamos indo para estar a olhar para a depuração. Não só estamos indo para falar sobre algumas técnicas, mas também vamos olhar para algumas das funcionalidades contidas dentro do IDE CS50 que permitem você depurar facilmente um programa. Apenas um exemplo de algo que pode dar errado e é realmente algo que já vimos antes. Neste caso, este é um programa C que aceita um número inteiro a partir do utilizador, divide por dois, e fornece a saída de volta para o usuário. Agora, a partir do que vimos anteriormente em palestras, sabemos que isso vai realmente causar tipos específicos de problemas de divisão quando temos números ímpares. Especificamente, nós vamos simplesmente jogar fora qualquer coisa depois do ponto decimal. Agora, nós sabemos que este passa a ser o caso. E se nós executá-lo, podemos confirmar nossas suspeitas, primeiro, compilando. E, em seguida, executando e inserindo um número ímpar. Isso não é novidade. Mas este é realmente um exemplo de um erro que pode existir dentro de um programa maior que se torna mais difícil de rastrear. Apesar de sabermos qual é o problema é, o verdadeiro cerne da questão pode estar tentando identificar especificamente onde o erro ocorre, identificar o que esse problema é, em seguida, fixando-o. Por isso, fornecer isso como um exemplo do que poderia ser algo que já sabemos, mas pode ser enterrado dentro de outros elementos do código. Assim, abrindo esta outra fonte arquivo de código como um exemplo, este problema de divisão é agora parte de um programa maior. Ainda pode ser um pouco pouco artificial, e nós pode ser capaz de facilmente identificá-lo, especialmente uma vez que estamos apenas discutindo isso. Mas podemos descobrir que esta problema pode existir em uma escala maior. Se eu compilar isso e agora executá-lo, digite um número ímpar, podemos ver que nós não conseguimos precisamente a saída que nós podemos ter esperado. Neste caso particular, poderíamos dizer que nós deseja contar todos os números de um a um número específico. E podemos ver que nós tem uma variedade de questões aqui se estiver saída, simplesmente, 0 e 1, quando nós fornecemos uma entrada de 5. Então, nós já sabemos que há um problema aqui. Mas nós não podemos saber com precisão onde este problema realmente existe. Agora, uma das maneiras que podemos tentar corrigir isso é algo que nós temos já foram introduzidas para. Nós podemos apenas usá-lo em uma escala maior. Na linha 14, temos esta função printf, o que nos permite imprimir o estado de várias peças de informação. E isso é algo que você deve alavancar dentro de seu programa para tentar descobrir exatamente o que é acontecendo em várias linhas de código. Assim, mesmo que este não é o resultado final que, na verdade, quer produzir fora do Neste programa, nós ainda pode ter alguma depuração declarações onde nós pode tentar descobrir exatamente o que está acontecendo dentro do nosso código. Portanto, neste caso, eu vou printf com o tag de depuração. Neste caso, esta é apenas uma seqüência de depuração que eu estou colocando-se para que se torne muito claro na saída do meu código o que é que eu quero mostrar. E aqui o número de saída que temos calculado. Neste caso, eu poderia quero saber exatamente o que está acontecendo antes e depois de alguns cálculos específicos. Então eu poderia usar um printf antes e depois de que a linha de código. Neste caso, eu poderia até mesmo torná-lo um pouco mais clara dizendo depuração antes e depuração depois de tanto que eu não confundir-me com várias linhas que parecem idênticas. Agora, se nós recompilar este e correr -lo, digite um número como cinco de novo, podemos ver que temos Agora saída antes e depois e achar que não temos feito uma clara divisão ou ter claro do número que nós realmente queremos fazer. Agora, neste caso, esta é não é realmente uma saída clara. Não é realmente um resultado claro que nós queremos fora deste programa particular. E isto é, novamente, uma pouco artificial. Mas, talvez, uma das coisas que nós poderíamos fazer se a especificação disse que queremos dividir isso por 2 e adicionar 1-- Assim, em outras palavras, queremos arredondar up-- seguida podemos saber que pudéssemos fazer essa coisa particular, neste caso. Agora aqui nós sabemos que seremos capaz de adicionar 1 para o nosso número para metade. Vamos recompilar este e confirmar que esta está se comportando da maneira que queremos. Podemos ver que agora antes tendo, temos o número 5. Depois de ter, que tem o número 3, que de acordo com nossa especificação, é o que queríamos fazer. Mas se olharmos para o saída aqui, nós podemos ver que poderíamos ter outro Preenchimento completo, que é que estamos começando a contagem a partir de 0. Agora, novamente, isso é algo que vimos no passado e que possamos corrigi muito facilmente. Mas, neste caso, nós também tinha o benefício de usar a instrução printf diretamente dentro do loop for para saber exatamente onde que o erro estava ocorrendo. Então, são declarações printf muito útil para ajudar a determinar onde, precisamente em seu código-fonte, um erro específico está ocorrendo. E é também importante perceber que, como estamos escrevendo código, nós poderíamos ter premissas sobre o estado de um programa. Ou talvez tenhamos premissas sobre o que parte do programa é realmente correto ou incorreto quando mais tarde, à medida que construímos sobre esse programa e torná-lo parte de uma complexo e programa maior que nos damos conta de que algum aspecto de que é realmente buggy. Usando printf pode realmente ajudar diminuir e identificar As regiões de um programa que não pode estar se comportando exatamente da maneira que nós esperar, com base em nossas suposições. Mas há outras ferramentas disponível, bem como, que nos permitem tentar figura para fora onde está ocorrendo um erro e também, especificamente, o que as coisas estão acontecendo dentro do programa. Então, usando printf é muito quando queremos útil para identificar áreas específicas de um programa que tem algum bug. Mas também se torna tedioso depois de um tempo. Neste caso, este é um programa relativamente simples com apenas uma ou duas variáveis. E torna-se muito fácil para nós imprimir o valor dessas variáveis no contexto do programa maior. Mas podemos ter um diferente programa que tem muitas variáveis. E isso não pode ser completamente tão fácil de usar printf para tentar avaliar o que está acontecendo para cada uma dessas variáveis como o programa está em execução. Há um programa que existe chamado um programa depurador. Neste caso, a que iremos uso é o depurador GNU, ou GDB, que nos permite inspecionar o interno funcionamento de um programa em um muito mais forma detalhada. Nós podemos realmente executar GDB da linha de comando aqui simplesmente digitando GDB eo comando que deseja depurar. Neste caso, contam. Agora, neste caso, podemos ver que ele nos leva a um prompt que diz GDB. E nós podemos, na verdade, executar comandos para GDB para efectivamente iniciar a execução do programa, pará-lo em certos pontos, avaliar as variáveis ​​e inspecionar as variáveis ​​que existe no estado de programa naquele momento em particular, e assim por diante. Ele fornece uma grande quantidade de energia para nós. Mas isso só acontece que o IDE também CS50 fornece uma interface gráfica ou um usuário interface para que o GDB nos permite fazer isso sem a necessidade de a interface de linha de comando que seja ou em tudo mesmo. A maneira que eu possa acessar esse é usando o botão de debug no topo do IDE CS50. Agora, no passado, o que nós temos visto é que nós usamos o comando linha para compilar e, em seguida, executar um programa. O botão de debug faz ambos os passos. Mas também vai trazer o guia depurador na extrema direita que nos permite inspecionar uma variedade de propriedades do programa como ele está sendo executado. Se eu clicar debug, neste caso, ele trará uma nova guia no console janela na parte inferior. E você pode ver que este guia tem algumas informações no topo. E podemos, em grande parte ignorar isso. Mas uma das coisas que queremos notar é que ela exiba a mesma coisa que nós obteria se tentamos executar o make em o programa C na janela do terminal. Aqui, podemos ver que está funcionando clang, e que tem uma variedade de bandeiras, e ele está compilando nosso arquivo count.c, que foi o guia selecionada no momento que eu bati de depuração. Então isso é muito útil porque agora usando este botão de depuração, podemos simultaneamente compilar e, em seguida, executar o programa que nós realmente deseja executar. Uma das bandeiras que é importante, neste caso, temos sido realmente usando por mais tempo mas também só fiz alguma mão acenando [inaudível], que é este aqui. Em clang, diz -ggdb3. Neste caso, o que somos dizendo clang, nosso compilador, é que nós queremos para compilar nosso programa. Mas também fornecer o que são chamado de informação símbolo para que o compilador realmente tem acesso para uma grande parte da informação subjacente contido dentro do programa. Mais especificamente, o número das funções que eu tenho, os nomes dessas funções, as variáveis, os tipos que as variáveis ​​são, e uma variedade de outras coisas que ajudam o depurador realizar o seu funcionamento. Agora há mais uma coisa isso é importante mencionar quando estamos discutindo running um programa deste modo. Observe que ele tem, na verdade, trouxe à tona uma nova guia no nosso console ao longo do fundo. Nós já não têm de interagir diretamente com a janela do terminal. Mas este novo guia é na verdade, uma janela de terminal. Ela só é específico para o funcionamento programa que criamos. Note-se que na parte inferior, em combinação com alguma saída por clang o compilador e GDB, qual podemos ignorar em grande parte, que realmente mostra a saída de nosso programa na parte inferior. Agora é importante perceber que esta janela realmente irá mostrar-lhe o saída de seu programa mas também pode aceitar a entrada para esse programa, bem. Então nota que diz Por favor insira um número, que é a mesma saída que tinha tinha na janela do terminal antes. Mas agora é mostrado nesta nova guia. I pode introduzir um número. E ele vai realmente função como esperamos mostrando nosso debug, de saída, a saída que pode ser buggy, como vimos antes. E bem no fundo, ele realmente tem alguma saída adicional do PIB apenas dizendo que este programa foi concluída. Agora, como você viu neste especial de execução por meio, não foi particularmente útil porque mesmo embora tivemos o menu depurador vir se, este ainda era um programa em execução. Em nenhum momento ele realmente pausar a execução para nós para ser capaz de inspecionar todos as variáveis ​​contidos. Há algo mais que temos que fazer, a fim para obter GDB reconhecer que nós queremos para pausar a execução do programa e não apenas permitir que ele continue normalmente como seria em qualquer outro caso. A fim de parar a execução, em alguma linha específica, precisamos criar o que é chamado um ponto de ruptura. E um ponto de quebra é muito facilmente criado neste CS50 IDE, tendo o seu rato e clicando directamente à esquerda de um número de linha específico. Uma vez que eu faço isso, um ponto vermelho aparece, o que indica que essa linha é agora um ponto de ruptura. E da próxima vez que eu corro GDB, esta irá parar a execução nesse ponto de quebra quando atinge essa linha de código. Agora, este é um importante coisa a perceber que não é necessariamente o caso em que cada linha de código na verdade, é acessível. Se eu fosse para criar uma função até aqui, para F-- vazio example-- e apenas fazer uma linha de impressão aqui-- Olá mundo-- se eu nunca chamar esta função, que será o caso de que, se eu definir um break point aqui, a função nunca será chamado. E, por conseguinte, este especial break point nunca realmente pausa a execução do programa. Então, digamos que eu criar corretamente um ponto de ruptura em algum linha de código que vai realmente ser executado. Agora, neste caso, esta é a primeira linha na função principal. Por isso, será certamente o caso que, assim que eu começar a execução, a primeira linha será alcançado. GDB vai parar a execução. E então, eu serei capaz de interagir com o depurador. Você pode definir várias linhas como pontos de interrupção, se você gostaria. Nós também podemos criar uma linha para cima aqui neste segmento de código que nunca será alcançado. E nós também pode definir uma mais abaixo. A razão que nós quero fazer isso nós vamos entrar em um pouco mais detalhes em apenas um momento. Então, por agora, deixe-me apenas desativar estes pontos de quebra adicionais para que possamos ver o que acontece quando eu tenho uma única pausa ponto no meu programa. Eu fiz alguns alterações a este programa. Então, eu preciso salvá-lo. Vou clique debug para que eu possa começar a compilação e, em seguida, execução do depurador. Veremos que, depois de momentos, os linha que foi selecionada como a quebra ponto é destacado em amarelo. Nós também podemos notar que no canto superior direito no painel de debug que o ícone de pausa se transformou em um pequeno ícone de jogo. Isto significa que temos de pausa execução, neste caso em particular. E apertar o botão Reproduzir se permitir-nos continuar a execução nesse ponto específico. Observe que há um par de outro botões disponíveis neste painel de depuração, também. Passar por cima, o que me permite executar que uma linha de código e passar por cima dessa linha para o seguinte, o que, neste caso, significaria que o printf instrução é executada. E, em seguida, fazer uma pausa execução na linha 13, assim. E há também um passo em função, que é útil se eu criei outro funções em outras partes do código-fonte. E eu quero entrar essas funções, em vez de executar essa função como um todo. Mas vamos olhar mais para o passo em função apenas em um momento. Agora notar algumas outras coisas que realmente existem dentro deste painel de depuração. Temos este painel de chamada chamar pilha, que nos mostra exatamente onde estamos. Neste caso, estamos dentro da função principal. Nosso script é chamado count.c. E nós acontecer de ser em linha 13, coluna um, que é precisamente o que a região em destaque do código fonte indica, como bem. Agora note que isso também mostra sob a seção variável local todas as variáveis ​​que existem dentro esta função. É importante notar que todas as variáveis vai aparecer nesta variável local seção dentro de uma função, mesmo antes de serem definidas. Podemos ver aqui que temos uma variável chamado num, tem um valor padrão de 0, e é do tipo int. Agora, antes de realmente inicializar todas estas variáveis, nós não somos necessariamente garantido para ver um valor de 0. E dependendo de outras execuções que você executou eo estado da sua memória quando você realmente executar este programa, você pode achar que você Não vejo valores de 0 e, em vez disso, alguns outros números loucos. Mas não se preocupe com isso. Não vai ser relevante até você realmente inicializar o valor. Agora, neste caso, podemos ver que Tenho realizado alguns saídas. E eu estou, agora, parou a execução. Mas neste caso, o que Eu realmente quero fazer é para o passo agora sobre esta linha de código para que eu possa realmente consultar o usuário para que int que nós queremos usar em nosso programa. Agora, neste caso, quando Eu bati passar por cima, aviso que a pausa ou melhor, o Resume botão mudou a este botão de pausa porque este código é realmente execução. O que está acontecendo neste momento é que é esperando por nós para entrada de algumas informações como podemos ver pelo nosso texto de saída na parte inferior. Então, agora, este é Não, na verdade, fez uma pausa, mesmo que, de certa forma, parece ser, porque nada está acontecendo. Mas isso só acontece que, meu caso específico na linha 13, Estou aguardando entrada do usuário. E assim GDB não é capaz de inspecionar um programa como ele está sendo executado. Agora, a próxima vez que eu entrar em alguns input-- então eu vou entrar nesse número 5, como vimos no past-- bater Return, e nós notar que, de imediato, pausas GDB e, novamente, destaca a linha seguinte. Mas note que agora, como um resultado da nossa introduzir um valor, nós atualizamos esse valor dentro de nossas variáveis ​​locais, que É muito útil saber com precisão o que esse número estava na memória. Agora posso permitir que este programa continue jogar até ao fim da sua execução por bater Resume. Podemos ver que muito rapidamente faz o acabamento programa de execução com a mesma saída que nós tinha antes, o depurador fecha, e agora este programa parou completamente. I mostram que apenas pela efeitos de ver o que acontece quando nós realmente bater Resume. Mas nós realmente estão indo para quero voltar para este programa para que possamos tentar depurar precisamente o que está acontecendo. Agora que eu estou usando o depurador, posso não precisa destas declarações de depuração printf. Assim eu poderia removê-los como eu vou fazer Agora, apenas para voltar para o nosso código mais simples que tivemos um momento atrás. Agora, quando eu salvar o programar e executá-lo, ele irá, mais uma vez, ir para aquele inicial ponto que eu tinha na linha 11 quebrar. E eu vou ser capaz de inspecionar meus variáveis ​​como eu quero fazer. Acontece que este parte não é muito interessante, E eu sei que eu vou imprimir esta declaração. Por favor insira um número. E então, eu sei que eu vou pedir ao usuário para esse inteiro. Então, talvez, eu realmente quero mudar a minha ponto de quebrar um pouco mais abaixo. Você pode remover pontos de interrupção clicando, novamente, diretamente para a esquerda de que o número da linha. Esse ponto vermelho desaparecerá, indicando que esse ponto de quebra é ido agora. Agora, no presente caso, execução foi pausada. E por isso não é realmente indo para retomar nesse caso particular. Mas eu posso definir uma pausa apontar um pouco mais tarde. E quando eu agora retomar a minha código, ele será retomada e dizer- a ponto de que ponto de quebra. Mais uma vez, eu bati Resume. Não parece que nada está acontecendo. Mas isso é porque minha código está aguardando entrada. Vou introduzir um número 5, pressione Enter, e Agora, o próximo ponto de interrupção será atingido. Agora, no presente caso, este é a linha de código que, antes, sabíamos passou a ser buggy. Então, vamos avaliar o que acontece neste momento particular no tempo. Quando uma linha é realçada, este A linha ainda não foi executado. Portanto, neste caso, podemos ver que eu tenho um número, que Eu tenho um número inteiro chamado Num que tem um valor 5. E eu vou estar realizando um pouco de matemática em que número. Se eu passar por cima disso, nós podemos perceber que o valor para o núm foi alterado de acordo com o aritmética que temos realmente feito. E agora que estamos dentro deste loop ou agora que o loop for em si está em destaque, vemos que temos um novo variável chamada i que vai ser utilizado na medida em que para o laço. Agora lembre-se antes que eu mencionou que às vezes você está vai ver algum tipo de louco números como padrão antes de esse número ou que é variável na verdade inicializado. Podemos ver que precisamente aqui na presente variável chamado I, que não possui Ainda não foi inicializado no momento de destacar. Mas podemos ver que ele tem algum número que não seria realmente esperar. Isso está ok. Não se preocupe com isso porque não temos, na verdade, inicializado esse número até que eu passar por cima desta linha eo valor i foi inicializado com o valor 1. Então, para ver que isso é, na verdade, o caso, vamos passar por cima. Podemos ver agora que esse A linha foi executada. E agora estamos destacando esta linha printf. E agora podemos ver como os nossos valores de i 3 e mudaram ao longo do tempo. Isto é muito útil para fazer, na verdade, é para passar por cima de linhas repetidamente. E você pode encontrar o que realmente acontece dentro de seu loop for eo que acontece com a variáveis ​​dentro desse loop como que a execução do programa ocorre um passo de cada vez. Agora, neste momento, eu passou por cima apenas o suficiente que agora eu estou no final do meu programa. Se eu passar por cima disso, ele vai realmente cessar execução como vimos no passado. Deixe-me reiniciar este, mais uma vez, de modo que eu possa apontar algo mais para fora, também. Neste caso, é agora me perguntar, de novo, para um número, o qual Eu vou, mais uma vez, entrar. Mas desta vez, eu vou entrar em um número maior para que o loop for irá interagir mais vezes. Neste caso, eu vou para introduzir um valor de 11. Agora, novamente, porque eu tinha definido um ponto de interrupção na linha 15, vai destacar essa linha. Podemos ver que o nosso número 11 é corretamente representados nos locais variáveis. Passando por cima disso, podemos agora veja o que acontece ao nosso valor de i à medida que prosseguimos dentro deste loop. Ele é incrementado cada vez que chegar ao topo do que para loop. Agora, uma das coisas que podem ser útil para fazer durante a execução deste programa é para mim, na verdade, mudar o variáveis ​​midstream para ver o que acontece com o meu programa. Neste caso, eu posso realmente clique duas vezes o valor. Observe que torna-se um campo de texto. Agora eu posso entrar diferente Valorizamos completamente para ver como o meu programa se comporta quando eu mudei essa variável. Agora, no presente caso, a variável i agora contém o valor 10. Mas o programa ainda é fez uma pausa na execução. Quando eu passar por cima, vejo que a valor i, onde entrei como 10, não é maior do que o valor de num, o que imediatamente faz com que o loop for para parar a execução. Agora que não é o único razão pela qual você faria quer modificar a variável no lugar. Você pode realmente querem para tentar modificá-lo para que possa continuar execução de um ciclo ou para que você possa modificar algum valor antes atinge algum conjunto específico de aritmética que você está prestes a realizar. Portanto, agora que nós realmente mudar a valor de i que o programa estava sendo executado, que causou o loop for para sair prematuramente, porque, de repente, eu Aconteceu a ser maior do que o valor de num, o que significa que esse loop não é mais necessário para ser executado. Além disso, ele passou a ser o caso que nós mudamos o valor de i quando a linha 17 foi destaque, que foi o ponto no tempo para que a execução do loop foi, na verdade, está sendo avaliado. Se eu tivesse alterado o valor da i em uma linha diferente, digamos 19, teríamos visto diferente comportamento porque linha 19 seria ter executado antes do loop condição foi reavaliada. Agora, neste momento, eu estou, novamente, no final deste programa. E posso permitir que isso continue a permitir que o meu programa para sair naturalmente. Mas há um par de coisas que são importantes para tirar a partir desta discussão especial. Você precisa avaliar suas próprias suposições sobre como o código deve estar se comportando. Toda vez que você pensar que alguma peça de código que você sabe acontece para trabalhar, que pode ser uma bandeira vermelha para ir para trás e avaliar, e ter a certeza que a sua assunção de como que o código está a funcionar é realmente verdade de como ele é expressa em seu código fonte. Mas ainda mais a questão era, quando estamos usando o depurador, você pode colocar pontos de interrupção em diferentes linhas de código, o que fará com que o depurador pausar a execução em cada uma dessas linhas para que você possa avaliar a memória ou até mesmo alterá-lo no lugar. E, novamente, lembre-se que você pode criar vários pontos de interrupção para que você Também pode retomar a execução, pule ao longo de grandes porções de código, e ele vai parar automaticamente no próximo ponto de quebra. Há realmente mais avançado recursos do depurador, bem. Mas nós vamos ter que encaminhá-lo para alguns vídeos subsequentes a fim de realmente provocar uma separação como para usar essas funções específicas. Por agora, obrigado muito para ver. E boa sorte depuração.