Olá e seja bem-vindo.
Nesta palestra, gostaria de falar sobre depuração.
Esse é um conceito muito importante que você precisa entender especificamente, você precisa entender algumas
das ferramentas e alguns dos métodos que pode empregar para encontrar e corrigir bugs.
Portanto, a depuração é o processo de encontrar e corrigir erros em um programa.
Normalmente, erros de lógica.
E vou lhe dizer a diferença entre erros de lógica e de sintaxe, mas isso também pode incluir erros de sintaxe
do compilador.
É o processo inteiro para erros de sintaxe.
Depurar é entender o que o compilador está lhe dizendo.
Você sempre deve se concentrar em corrigir o primeiro problema detectado.
Quero examinar alguns dos erros e avisos comuns do compilador que você pode receber, mas uma
das coisas que você sempre quer saber sobre o compilador é que ele pode apresentar vários
erros.
Você sempre deve se concentrar no primeiro erro, pois esse primeiro erro pode resultar em outros erros.
Portanto, se você vir cinco erros em um programa que o compilador está informando, pode ser que haja apenas um erro
e esse primeiro erro pode estar causando os outros erros.
Portanto, é importante sempre observar o primeiro erro principal e corrigi-lo primeiro.
Nesse exemplo muito simplista, temos vários erros nesse código.
Não temos um.
Não temos um colchete esquerdo inicial, uma chave.
Não temos um ponto e vírgula.
Estamos retornando uma string.
Portanto, quando salvamos isso e vamos compilá-lo e você vê alguns erros
em vermelho, quando vamos compilá-lo, ele nos diz que temos três erros.
Você nunca deve selecionar apenas o último erro na linha sete, mesmo que, nesse caso, seja um erro real.
Você sempre quer corrigir apenas o primeiro erro.
Então, você vê o primeiro erro listado aqui, corrige esse primeiro erro, recompila e executa novamente.
Mas o motivo para fazer isso é apenas para corrigir o erro de nível superior, pois esse erro pode causar outros
problemas.
Portanto, concentre-se sempre em corrigir o primeiro erro. A depuração e o processo de localização e correção de bugs podem ser muito
complexos, desde a localização de erros simples até a coleta de grandes quantidades de dados para análise.
Tudo depende.
Às vezes, você não tem nenhuma informação fornecida, exceto o registro, e, nesse caso, precisa analisar
as coisas.
Às vezes, você terá bugs que não podem ser repetidos.
Não é possível reproduzi-lo porque ele acontece em uma ocasião muito rara.
Geralmente, é quando você precisa analisar os registros ou alguma variação disso.
A capacidade de depuração de um programador é uma habilidade essencial.
É realmente uma habilidade de resolução de problemas que pode lhe poupar muito tempo e dinheiro.
Portanto, essa é uma habilidade importante a ser aprendida.
É uma habilidade importante a ser desenvolvida.
Isso requer prática.
Você não vai simplesmente vir até você.
Há certas técnicas e métodos que você pode aplicar para melhorar
a depuração.
Conheço vários desenvolvedores que são realmente bons em escrever código de software.
Eles entendem muito bem a linguagem, mas suas habilidades de depuração são muito ruins.
Eles podem encontrar um bug e sua primeira inclinação para resolvê-lo é realmente meio fora do caminho.
E, novamente, isso vem com a prática.
Você ficará cada vez melhor na depuração e na compreensão de como encontrar e corrigir problemas, mas
isso é algo que você também precisa aprender.
A fase de manutenção é a fase mais cara do ciclo de vida do software.
A fase de manutenção ocorre depois que você lança seu produto e agora precisa mantê-lo.
Mantê-lo significa corrigir todos os novos bugs que surgirem e também adicionar novos recursos.
Assim, por exemplo, quando o Windows lança um novo sistema operacional, como o Windows 10, eles não terminam de
produzir esse software.
Ele ficará em campo por provavelmente dez anos.
E a Microsoft ainda é responsável pela correção de novos bugs ou pela descoberta de novos bugs para esse software.
E é por isso que eles promovem atualizações automáticas e fazem coisas desse tipo.
E eles também perceberão que o sistema operacional lançará novos recursos à medida que o mercado
e a tecnologia mudarem.
Portanto, a manutenção é muito importante.
Por isso, é muito importante encontrar e corrigir seus bugs.
Entenda, porém, que os bugs são inevitáveis.
Todos os softwares têm bugs.
Ninguém pode escrever um código perfeito e nem se trata necessariamente de escrever um código perfeito.
É que as coisas mudam com o tempo.
Portanto, isso também pode resultar em bugs.
Você precisa entender que o fato de um software ter bugs não significa que ele seja um código mal escrito.
Isso é inevitável.
Portanto, não há problema em ter bugs, sabe, quando você os encontra e os corrige, é ainda melhor porque você realmente
sente que está realizando algo.
Problemas comuns.
E vamos falar mais sobre problemas específicos relacionados a ver.
Mas os problemas comuns quando se está tentando depurar são erros de lógica.
Os erros de lógica não têm a ver com o oposto do tipo de erros de sintaxe que seu código realmente compila.
Ele funciona, mas não faz o que deveria fazer.
Portanto, há algum tipo de lógica ou funcionalidade pretendida que está incorreta.
Talvez você tenha uma instrução IF e esteja pulando para essa instrução if na condição errada.
Talvez você tenha um loop e ele não esteja sendo executado o número certo de vezes ou não esteja sendo classificado corretamente.
A funcionalidade está quebrada, mas o compilador diz: "Ei, tudo parece bem".
Aqui está um exemplo de erro de lógica.
Um erro lógico é quando a sintaxe está correta, mas os resultados desejados não estão corretos devido a algum tipo de problema
lógico.
Portanto, nesse código, você vê que o loop for tem um ponto e vírgula no final, o que é sintaticamente correto.
Mas você está esperando que esse loop for seja executado cinco vezes.
Mas não é.
Ele terá apenas um tempo de execução.
Portanto, se dissermos construir e construirmos esse projeto, veremos um aviso, o que é bom, e você deve sempre corrigir todos
os avisos.
Mas a compilação foi bem-sucedida.
Portanto, quando executarmos esse projeto, você perceberá que ele é executado apenas uma vez e imprime
o Hello World uma vez.
Portanto, esse é um exemplo de erro lógico.
Os erros de sintaxe estão diretamente relacionados ao compilador.
São coisas que não seguem as regras do idioma.
Não se deve colocar ponto e vírgula no final de uma declaração.
Você não tem um final, certo?
Parênteses com uma esquerda, parênteses.
Está faltando um suporte.
Você não está usando a palavra-chave correta, coisas desse tipo.
Os erros de sintaxe geralmente são causados pelo compilador. Os erros de lógica serão causados pelo programador
durante a fase de teste.
A corrupção de memória também é um problema comum, especialmente em C.
Se você não estiver liberando a memória depois de alocá-la corretamente, poderá
ter coisas como referências pendentes e também problemas de memória relacionados à memória que não está sendo usada.
Você pode ter vazamentos de memória.
Aqui está um exemplo de erro de memória.
Nesse código específico, temos de fato um vazamento de memória, portanto, ainda não falamos sobre ponteiros.
Ainda não falamos sobre alocação dinâmica de memória.
Mas em C é possível alocar memória enquanto o programa está em execução.
Você pode usar uma função malloc.
Portanto, aqui estamos marcando, alocando memória, mas nunca a liberando.
Portanto, podemos criar esse código, executá-lo e tudo está pronto para funcionar, mas não estamos realmente liberando
esse erro.
Portanto, o que teríamos de fazer para corrigir esse erro de memória é dizer free e, em seguida, dizer
free e X para liberar esse ponteiro.
Mas sempre que você aloca memória, você não a libera.
Isso é um vazamento de memória.
E outro exemplo seria se não alocarmos a memória, se criarmos um ponteiro e tentarmos atribuí-lo,
teremos corrupção de memória porque essa memória não tem nenhuma alocação para ela e estamos
tentando atribuir um valor a ela.
Portanto, isso é apenas uma pequena amostra de alguns exemplos de memória, corrupção, desempenho e escalabilidade.
Esse pode ser um problema em que sua funcionalidade é pretendida e seu código está sintaticamente correto,
mas não é muito eficiente ou você não tem o hardware para suportá-lo.
Portanto, se você estiver executando um servidor e não tiver hardware suficiente para suportar todas as solicitações, ele será
executado de forma muito, muito lenta.
Ou se você escreveu seu programa para trabalhar com dez usuários, mas quando ele recebe muitos usuários, talvez não
seja tão rápido porque o banco de dados fica atolado ou qualquer que seja o caso.
O código funciona, mas não foi projetado corretamente para lidar com muitas e muitas quantidades de usuários ou qualquer
que seja o caso.
Falta de coesão.
Isso pode ser um problema em seu código que reduz a qualidade relacionada ao fato de seu código estar fazendo coisas demais ou de uma função
estar fazendo coisas demais.
Não se trata de um único foco ou até mesmo de um aplicativo com funcionalidade excessiva, com recursos
que nunca são usados e, portanto, isso provavelmente exigiria um novo design.
Aqui está um exemplo de um programa que não seria modularizado, que não é coeso.
Você tem uma grande função que está fazendo muitas coisas, centenas de linhas de código, milhares de linhas
de código.
Você deseja dividir uma função em funções auxiliares, módulos separados.
Isso torna seu programa muito mais coeso e fácil de manter.
Assim, em vez de ter um grande método principal que faz a inicialização, lê a entrada, encontra alguns, encontra
a diferença, encontra o produto, encontra o quociente, você pode criar todo esse código e colocá-lo em módulos auxiliares
e separar o código, de modo que a separação possa ajudar a encontrar e corrigir os erros com muito mais facilidade.
A coesão é sempre importante em C e geralmente é resolvida quando você tem alta coesão fornecendo métodos
auxiliares.
Acoplamento apertado.
Isso também tem a ver com a qualidade do código.
É aqui que você tem muitas dependências.
Acoplamento é diferente de coesão.
A coesão é seu foco único.
O acoplamento é quando você tem dependências entre funções ou entre objetos.
Falamos sobre acoplamento, falamos sobre variáveis globais.
Se uma variável global estiver sendo usada em várias funções diferentes, ela poderá causar dependências
entre essas funções.
Portanto, o que você deve buscar quando escreve código e está escrevendo código de alta qualidade
é ter uma coesão muito alta e um acoplamento muito baixo.
O processo de depuração.
A primeira coisa que você deve fazer antes de localizar e corrigir qualquer erro é entender
o problema.
Isso está relacionado a não mergulhar de cabeça e começar a examinar o código, sentar-se com o testador, entender
os requisitos, entender o que está acontecendo.
Porque se você não entender o que está acontecendo, não conseguirá.
Você não deve nem mesmo tentar solucioná-lo.
Depois de entender o bug, tente reproduzi-lo, pois a reprodução do bug pode ajudá-lo a obter mais
informações.
Nem sempre é possível reproduzir os bugs porque eles podem ocorrer em circunstâncias realmente raras.
Assim, por exemplo, os bugs que têm condições de corrida ou até mesmo corrupção de memória podem ocorrer apenas
em casos de uso muito específicos e podem ser problemas intermitentes.
E esse é o caso?
E você não pode reproduzi-lo.
Será cada vez mais difícil depurar.
Se você conseguir reproduzir o problema, geralmente poderá obter informações suficientes para corrigi-lo.
A maioria dos problemas pode ser corrigida.
Além disso, você pode ter problemas com a impossibilidade de reproduzir um problema se estiver em um ambiente com vários threads,
processos paralelos ou problemas de threading.
Esses também são muito difíceis de depurar, mas, definitivamente, tente reproduzi-los depois
de descobrir qual é o problema em termos de capacidade de entender o problema e reproduzi-lo. Se não conseguir,
tente aplicar algum tipo de abordagem do tipo dividir para conquistar.
Você quer tentar isolar o bug, simplificar o problema, porque a capacidade de reduzir vai lhe dar informações,
mas talvez não diga necessariamente em que parte do código está o bug.
Portanto, o que você pode fazer para tentar simplificar o problema é remover partes do caso de teste original para que
não esteja testando demais.
Você pode isolar o problema ainda menos dizendo: "Ah, isso só acontece nessa situação específica".
Você também pode comentar o código.
Falamos sobre funções que têm apenas stubs e não têm implementação.
Às vezes, isso é empregado em testes para limitar o conjunto de problemas.
Além disso, se você comentar o código, terá menos código onde o bug pode realmente ocorrer.
Portanto, à medida que você comenta o código ou faz retrocessos nas alterações, o bug pode desaparecer e, se o bug desaparecer,
isso indica que o código que você comentou pode ter sido a causa dele.
No entanto, o que você quer fazer essencialmente é transformar um programa grande em vários programas pequenos, seja
por meio de testes ou de comentários.
Outra técnica comum é a realização de testes unitários em seu código.
Se um teste de unidade falhar, isso pode lhe dizer exatamente onde a lógica ou o problema pode estar em seu código.
Depois de isolar o bug, isso o ajudará a encontrar a origem do problema no código, o código específico que
está causando o problema.
Às vezes, isso requer o uso de ferramentas de depuração, como um depurador.
Falaremos sobre um depurador em detalhes, mas, geralmente, dependendo do IDE de desenvolvimento integrado
que você usa, há depuradores que vêm com o IDE.
Há também depuradores que podem ser autônomos.
O depurador mais importante que usaremos na programação em C chama-se GDB.
É o depurador GNU, portanto, é uma ferramenta interessante que, na verdade, é uma ferramenta gráfica na qual você pode
interromper a execução do programa e adicionar pontos de interrupção.
E, ao interromper a execução, você pode ir linha por linha do seu código e observar o valor das variáveis.
Assim, você pode ver exatamente o que está acontecendo no seu código enquanto ele está sendo executado.
É uma ferramenta de valor inestimável.
É que ele é muito útil.
Vamos executar o depurador.
Mostrarei a você como adicionar pontos de interrupção.
Mostrarei como configurar um relógio para que você possa observar as variáveis à medida que elas mudam e também percorrer
o código.
Depois de identificar a origem do problema, você precisa corrigi-lo.
Isso requer muita experiência e prática.
Mesmo que você tenha encontrado a origem do problema, talvez não consiga corrigi-lo apenas com
base em sua experiência.
Isso às vezes inclui o redesenho ou a refatoração de outro código que, especialmente com problemas de escalabilidade
ou de desempenho, pode ser necessário redesenhar para permitir que o software seja mais escalável.
Portanto, nem sempre é fácil apenas encontrar o problema e corrigi-lo.
O problema, às vezes, é que problemas ainda mais difíceis, como threading ou processamento paralelo,
são ainda mais difíceis de corrigir.
Mas, à medida que você escrever mais e mais códigos, isso se tornará mais fácil de fazer.
E, por fim, você precisa fazer muitos testes.
Sempre teste muitos limites diferentes.
Casos bons, casos ruins e casos intermediários para verificar se você realmente corrigiu o problema.
Algumas das técnicas e ferramentas disponíveis.
Uma ferramenta comum e muito, muito usada é o uso de instruções de impressão.
Você pode usar instruções de impressão para basicamente rastrear seu código.
Portanto, em C, você pode usar a instrução print F e colocá-la em várias linhas do seu código para indicar
onde você está.
Isso também é útil, muito útil quando seu programa trava.
Isso pode ajudar a identificar onde houve o travamento.
Ao adicionar essas instruções de impressão ao rastrear o código, você pode adicionar a saída nas instruções
de impressão para ver o valor das variáveis, pois o valor das variáveis pode afetar o estado e causar
problemas de lógica.
Portanto, apenas adicionar instruções de impressão é muito, muito útil.
Ele pode mostrar o fluxo de execução e ajudar a isolar o erro.
Os depuradores, como mencionei no GDB, podem monitorar a execução do programa.
Você pode interrompê-lo, executar linha por linha, reiniciá-lo, definir pontos de interrupção, observar variáveis e memória.
Muito, muito útil ao tentar diagnosticar a origem de um problema.
Arquivos de registro.
Os arquivos de registro também são muito úteis, especialmente para bugs que não podem ser recriados.
Você pode analisá-los posteriormente para ver exatamente o que estava acontecendo quando o bug ocorreu.
Isso significa que você precisa fornecer boas declarações de registro em seu próprio código
para que isso seja útil.
Portanto, você pode criar seu próprio arquivo de log e gravar nesse log enquanto o programa estiver em execução para que, se houver um erro,
você possa enviar esses logs para os desenvolvedores.
Empregamos isso bastante na Xerox porque, quando encontramos problemas, nem sempre podemos reproduzi-los ou eles
podem ser encontrados no campo.
Precisamos ter acesso a esses registros para ver o que estava acontecendo quando o bug ocorreu.
Software de monitoramento.
Há um software que pode monitorar e analisar o tempo de execução do uso da memória, a ameaça do tráfego de rede e as informações
do objeto.
Isso pode lhe dizer se você está alocando muita memória e não a está excluindo.
Se você tiver uma ferramenta para o tráfego de rede de uso de memória que possa dizer se você está
enviando muitos pacotes ou se o seu software é escalonável, se houver um aumento no tráfego de
rede e você perceber que o tempo de resposta está diminuindo.
Você também pode monitorar informações de threads e objetos para processamento paralelo para ver o que está acontecendo em determinados
threads e o que está acontecendo em outro thread.
Isso o ajudará a diagnosticar qualquer tipo de problema no fio.
Algumas outras ferramentas de depuração comuns que você pode usar para localizar e corrigir seus erros.
Você pode usar o tratamento de exceções.
Muitas linguagens de programação têm tratamento de exceções.
Infelizmente, o C não tem um manipulador de exceções que realmente evite falhas e faça muitas coisas no tempo
de execução.
Isso não se aplica a nós, mas ainda é bom entender.
Blocos Trycatch, coisas desse tipo.
Analisadores estáticos.
Há muitos analisadores estáticos disponíveis para analisadores estáticos de C.
Analisar o código-fonte sem o executável real.
A execução do programa não precisa ser executada.
Ele percorre linha por linha e examina o código-fonte.
E, ao analisar o código-fonte, ele procura por problemas conhecidos.
Assim, ele pode verificar a semântica.
Ele pode garantir isso.
É isso aí.
Você não terá nenhuma variável não inicializada se estiver alocando nova memória e a excluir.
Se houver algum código morto em que o código não esteja sendo realmente executado, você poderá excluir esse código.
Ele também analisa coisas como condições de corrida e deadlocks.
No meu trabalho, usamos alguns analisadores estáticos.
Temos uma ferramenta chamada Coverity, que é bastante popular, e há uma ferramenta de code sonar que também usamos, e a executamos
em toda a nossa linha de base, em todo o nosso conjunto de fontes.
E sempre que introduzirmos novos arquivos e novos códigos, essa ferramenta será executada nesse código e poderá identificar problemas
imediatos com o código no que diz respeito a variáveis não inicializadas, vazamentos de memória e assim por diante.
Ele não analisa a sintaxe.
É para isso que serve o compilador.
Há também vários conjuntos de testes que executam um conjunto de testes abrangentes de ponta a ponta do sistema que você
pode executar em seu software.
Os testes são sempre importantes, mas se você puder automatizá-los de alguma forma para executar determinados
testes sempre que fizer uma nova compilação, por exemplo, isso pode ser muito útil e pode realmente evitar
erros, mas também ajuda a diagnosticar erros.
Depuração do programa após o travamento.
Vamos falar sobre algo chamado pilha de chamadas.
Esse é um mecanismo muito útil, especialmente em uma linguagem como C, em que você tem uma falha e a única
informação que obtém é algo como falha de segmentação.
Quando há esse travamento, o que acontece muitas vezes, dependendo de como o programa travou, um arquivo de
núcleo é gerado e você pode analisar o arquivo de núcleo para examinar a pilha de chamadas e ver exatamente onde o programa
morreu.
Ele informará a linha de código em que o programa morreu.
Portanto, vamos aprender sobre a pilha de chamadas.
E quanto à análise dos arquivos principais, essa é uma técnica mais avançada.
Mas em C, um arquivo de núcleo pode ser gerado e você pode analisá-lo.
Essa é uma técnica um pouco mais avançada, mas é outra maneira de fazer a depuração em seu software.
E quanto à prevenção de erros?
Sabemos que a depuração consiste em encontrar e corrigir erros, mas se você puder evitar erros, será ainda melhor.
Uma das maneiras de evitar erros é escrever um código de alta qualidade.
Esse realmente não é o foco de muitos engenheiros de software.
Muitos engenheiros de software escrevem o código e, desde que ele funcione, é tudo com
o que se preocupam, ou desde que o resultado esteja correto, eles se consideram realizados.
Mas existe algo como código de baixa qualidade, e você não quer escrever isso.
Demonstrei alguns desses aspectos durante as palestras, mas coisas como o uso de nomes de variáveis significativos são
muito importantes.
Ou se você escrever um código ineficiente em que esteja usando loops talvez mais do que o necessário, ou usando uma
instrução if ou não precisa fazer isso.
Todos esses tipos de coisas constituem um código de baixa qualidade.
Se você tem uma função que faz 100 outras coisas e não é coesa, se você tem 5.000 linhas em uma função,
se o seu código não está organizado de forma modular ou se você tem muitas dependências
entre funções, novamente, tudo isso é código de baixa qualidade e você deve se esforçar para não fazer isso.
Você deseja empregar técnicas em que seu programa seja organizado.
É fácil de manter, é altamente coeso, tem baixo acoplamento e você usa nomes de variáveis significativos.
Muito, muito importante.
Novamente, isso é muito importante também porque a fase de manutenção é muito, muito cara.
Se você escrever um código de baixa qualidade, será muito mais difícil mantê-lo.
É mais difícil isolar erros e adicionar novos recursos.
Portanto, use técnicas de boa qualidade ao escrever seu código.
Testes unitários.
Os testes unitários ajudam muito a evitar erros.
Os testes de unidade geralmente são executados automaticamente quando você compila testes de unidade, o que pode ajudar a evitar regressões.
Esse é um dos fatores mais importantes.
Regressão é quando você escreve um novo código e introduz um bug em algo que estava funcionando anteriormente.
Você quebrou o código antigo.
Você não quer fazer isso.
Se um software estiver funcionando e algo novo que você fez o quebrar, você quer ser notificado imediatamente, e os
testes de unidade podem ajudar a fazer isso porque os testes de unidade serão executados no código antigo e dirão: "Ei, isso estava
funcionando, agora está falhando".
Ele também pode encontrar erros no novo código antes que ele seja entregue, porque, novamente, eles são executados após a compilação
e ele informa imediatamente que há algo errado.
Há um conceito de desenvolvimento orientado por testes que está se tornando muito, muito popular.
É aqui que você escreve os testes primeiro e, depois, desenvolve o código.
E o objetivo aqui é que, quando você escrever o primeiro teste, ele falhe e você escreva o código para
que ele passe.
Há muitas vantagens no TDD e ele é uma espécie de maneira inversa de pensar.
Normalmente, quando as pessoas escrevem código, elas escrevem o código primeiro e depois escrevem o teste.
Mas, com o TDD, você tem uma maneira diferente de pensar, de modo que trabalha em partes realmente
pequenas e tende a escrever um código muito coeso e não tem o código, não tem muitas dependências
porque, adivinhe?
Se você puder testar o código, isso significa que ele não tem tantas dependências.
Se o seu teste unitário for muito, muito grande e você estiver simulando todas essas coisas diferentes,
isso geralmente é um grande indicador de que seu código é muito, muito dependente de outras coisas.
Portanto, ao empregar o TDD, você é forçado a escrever um código com acoplamento muito frouxo, que não tenha dependências,
e é forçado a ter funções muito coesas.
Há muitas vantagens no TDD.
Também é necessário fornecer uma boa documentação e fazer um planejamento adequado.
Não basta mergulhar no código e começar a implementar.
Escreva seu projeto em um papel.
Utilize pseudocódigo.
Certo.
Planeje seu código.
Se você planejar o seu código, entender os requisitos e tiver um bom projeto bem pensado,
poderá evitar muitos erros.
E já mencionei isso ao longo do curso, mas é muito, muito importante ter uma abordagem do tipo
dividir para conquistar, trabalhar em pequenas etapas e testar constantemente após cada etapa, não escrever 1.000 linhas
de código e depois dizer, ok, compilar e testar.
Essa não é uma boa abordagem.
Evite muitas mudanças de uma só vez.
Ao fazer alterações, aplique-as de forma incremental, adicione uma alteração e, em seguida, teste completamente antes de iniciar
a próxima etapa.
Isso pode ajudar a reduzir todos os tipos de erros e também limita seu conjunto de problemas.
Portanto, se houver um bug, você saberá imediatamente onde ele está no código, pois acabou de
compilar e adicionar aquela linha ou número de linhas.
Mais uma vez, não posso enfatizar a importância da depuração, da escrita de código de alta qualidade e da prevenção de erros.
Essas técnicas de solução de problemas economizarão muito tempo e farão de você um programador de primeira linha
muito melhor.
Portanto, nas próximas palestras, quero falar sobre alguns dos erros comuns que vemos na linguagem de programação
c c.
Além disso, gostaria de falar um pouco sobre a pilha de chamadas.
Quero examinar o depurador e apresentar as opções de menu e como você pode parar e executar os relógios
e assim por diante.
E também quero ajudar a identificar alguns erros comuns do compilador para que você veja como interpretar os compiladores, os avisos e os
erros do compilador.
O que significa se o compilador disser isso?
O que significa se o compilador emitir esse aviso?
Obrigado.