Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

R peralva/debugging #425

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions content/brazilian-portuguese/debugging/01-overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
title: "Uma visão geral da depuração"
difficulty: "Intermediário"
weight: 2
draft: false
---

Existem muitas maneiras diferentes de depurar um programa. Neste guia, começaremos com algumas etapas gerais para que você não se perca ao depurar um programa. Existem 3 etapas básicas para depurar um programa:

1. **Identifique o problema**. Nesta etapa, seu objetivo é identificar o que está causando o bug. Isso envolve encontrar as condições que levaram ao bug. Ser capaz de reproduzir o bug fornece algumas das informações necessárias, o que permite restringir o problema e talvez ajudar a entender por que o bug está ocorrendo.

2. **Encontre uma solução**. Nesta etapa, seu objetivo é corrigir o bug. Às vezes este é o passo mais fácil, mas outras vezes pode ser difícil porque você pode descobrir que a lógica do seu programa estava incorreta para começar! Esta etapa combinada com a etapa 3 pode facilmente ser a que vai levar mais tempo.

3. **Teste a solução**. De que adiantará se a sua correção não resolver o bug? Você também precisa ter certeza de que sua correção não introduz mais bugs. Esta etapa pode ser tão simples quanto recompilar e executar o programa novamente depois de aplicar a correção. Ou pode envolver a execução de um conjunto completo de testes automatizados em um ambiente. De qualquer forma, o objetivo desta etapa é estabelecer a *correção* do programa.

Provavelmente a parte mais frustrante da depuração é a etapa 1. Como programadores, nosso trabalho é escrever código, mas não podemos escrever código para resolver o problema se não soubermos qual é o problema! Assim, a maior parte deste workshop se concentrará em descobrir onde um bug pode estar escondido, bem como em como você pode usar algumas ferramentas para auxiliar em sua busca.
72 changes: 72 additions & 0 deletions content/brazilian-portuguese/debugging/02-identify-the-problem1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
title: "Passo 1 – Encontrando o problema (Parte 1)"
difficulty: "Intermediário"
weight: 3
draft: false
---

## Erros do compilador

Vamos começar e dar uma olhada em um dos primeiros problemas assustadores que um programador novato enfrenta: erros de **leitura** e **compreensão**.

Existem dois tipos de erros: erros de **compilador** e de **tempo de execução**.

Um erro do **compilador** geralmente indica um problema com sua sintaxe. Talvez você quisesse expressar uma ideia no programa, mas não seguiu as regras da linguagem de programação. Eles são capturados quando você compila seu programa. Erros do compilador são relativamente fáceis de corrigir. Compiladores como `gcc` geralmente fornecem muitas informações sobre o que deu errado quando o código é compilado. Vamos explorar alguns desses erros.

Abra a guia **Shell** no programa Replit abaixo e execute o seguinte comando:

```bash
make CompilerErrors
```

{{% panel theme="info" header="Note"%}}
Neste exemplo, não executaremos o programa - este programa tem como objetivo mostrar alguns erros comuns do compilador que você pode encontrar.

{{% /panel %}}

<a class="my-2 mx-4 btn btn-info" href="https://replit.com/@nuevofoundation/Debugging-Samples-C" target="_blank">Iniciar Replit</a>

O primeiro erro deve ser parecido com este (ou semelhante):

![Erro 1: ponto e vírgula ausente](../resources/w2-01.png)


Você notará que o erro contém:
- o nome do arquivo que tentamos compilar (**`CompilerErrors.c`**).
- a linha onde o erro foi encontrado (**`12`**).
- a posição na linha onde o erro está localizado (**`19`**).

Como você pode ver, o compilador fornece a linha de código e aponta onde o erro realmente ocorreu! Em seguida, fornece um breve nome do erro - neste caso, o compilador esperava um ponto e vírgula (**`;`**) no final da linha. Você pode simplesmente adicionar um ponto e vírgula para corrigir esse erro.

{{% notice warning %}}
O outro erro menciona que há uma chave ausente (**`}`**) quando não era esperada. A correção para este também é relativamente simples: você pode adicionar a chave correspondente `{` ao lado de `func()`.
{{% /notice %}}

Depois de consertar, você pode compilar o código novamente usando o mesmo comando.

```bash
make CompilerErrors
```

Espere, há mais erros! Conforme mencionado na caixa de aviso, o compilador não é ótimo em detectar erros se houver uma chave ausente. Um erro do compilador pode “ocultar” outros.

Usaremos uma tabela para mostrar erros comuns do compilador e suas causas gerais.

| Erro | Causa | Correção Geral |
|---|---|---|
| Ponto e vírgula ausente | Faltando ponto e vírgula. | Adicione o ponto e vírgula `;`.|
| Corpo da função esperado após o declarador da função | Colchete `{` ausente, geralmente no início de uma declaração do corpo da função. | Adicione a chave `{` onde ela pertence. |
| Identificador esperado ou '('| No contexto de uma chave, geralmente significa que você tem uma chave extra pendurada `{`. | Remova a chave ou adicione um `}` correspondente depois dela.|
| Redefinição de... | Em algum lugar do seu código você declarou uma variável. Mais tarde você declarou isso novamente. | Renomeie as variáveis ou remova uma delas. |
| Uso de tipo não declarado... | O compilador não consegue encontrar a declaração do tipo que você deseja usar. | Geralmente isso vem de diretivas `#include` inadequadas (já que os tipos geralmente são declarados em arquivos de cabeçalho). Certifique-se de que não haja erros de digitação. |
| Deve usar a tag `struct` (ou `enum`) | C requer que você use `struct NAME_OF_TYPE` ou `enum NAME_OF_ENUM` sempre que quiser se referir ao tipo struct/enum. | Normalmente os desenvolvedores C usam uma instrução `typedef` para que não precisem digitar `struct` ou `enum` para se referir a esses tipos. No entanto, você mesmo pode adicionar essas palavras-chave. |
| Nenhum membro nomeado... | Na sua `struct`, não há campo com o nome que você solicitou. | Provavelmente um erro de digitação ou você ainda não definiu um campo em sua `struct`. |
| Tipos incompatíveis... | Você está misturando e combinando tipos, o que não é permitido em C sem conversão explícita. | Verifique se uma atribuição está precisa ou use conversão explícita para silenciar o erro durante o tempo de compilação. Isso pode levar a erros de tempo de execução. |
| O tipo de argumento está incompleto | Se uma função retornar nula, você não poderá passá-la como argumento para outra função! | Corrija a declaração da função como não nula e certifique-se de que deseja tal comportamento. |
| Estranho '(' ou ')' antes de ';' | Parênteses incompatíveis `()` | Você tem um conjunto extra de parênteses em algum lugar. Verifique novamente para ter certeza de que há um par correspondente para cada um deles! |

Se a aplicação de uma correção geral não funcionar, *então* você deve procurar ajuda na Internet. Porém, saber ler o erro e decifrar o que ele deve consertar é importante! Você não deveria precisar procurar erros comuns com soluções simples.

{{% notice warning %}}
Nem todos os compiladores fornecem informações úteis. Por exemplo, os erros do compilador C++ podem variar desde erros simples (como em `CompilerErrors.c`) até enormes blocos de texto, especialmente ao trabalhar com modelos. O objetivo desta lição NÃO é afastá-lo completamente das fontes da Internet, mas sim familiarizá-lo o suficiente com elas para lhe dar mais intuição sobre como corrigir erros simples.
{{% /notice %}}
98 changes: 98 additions & 0 deletions content/brazilian-portuguese/debugging/03-identify-the-problem2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
title: "Passo 1 - Encontrando o problema (Parte 2)"
difficulty: "Intermediário"
weight: 4
draft: false
---

## Imprimir extratos

Erros de compilador são uma coisa, mas erros e bugs de tempo de execução são outra. Embora o compilador possa facilmente dizer onde procurar, os bugs de tempo de execução são causados pela forma como o seu programa é executado. Precisamos entender o que o programa está fazendo quando o bug acontece:
- Quais variáveis estão sendo usadas?
- Qual declaração está sendo chamada?
- Há alguma declaração faltando que precisávamos?

Em aplicações menores, podemos usar declarações print no código para descobrir rapidamente o estado de execução do programa. As declarações impressas são uma maneira rápida e suja de examinar um programa enquanto ele está em execução e, com sorte, você será capaz de descobrir o que está causando o bug sem muita dificuldade.

## Pesquisa binária

Um dos algoritmos mais simples que você aprenderá ou aprendeu é a pesquisa binária, que permite pesquisar um item em uma lista ordenada em tempo logarítmico. A ideia é verificar o meio da lista ordenada e ver se corresponde ao elemento que desejamos; se encontrarmos o elemento, o algoritmo estará concluído. Se o elemento for superior, pesquisamos na metade superior da lista. Caso contrário, pesquisaremos na metade inferior da lista. Repetimos o processo até encontrarmos o item que procuramos.

|![Procurando o número 7 em uma lista ordenada de 10 números usando a pesquisa binária](../resources/binary_search.svg)|
|:--:|
|Pesquisando o número **7** em uma lista ordenada de **10** números usando Pesquisa Binária |

<a class="my-2 mx-4 btn btn-info" href="https://replit.com/@nuevofoundation/Debugging-Samples-C" target="_blank">Iniciar Replit</a>

Nosso programa solicitará que você pesquise um nome com base na posição em que ele está localizado.

Abra o **Shell** no programa Replit e compile o programa:
``` bash
make BinarySearch
```

Execute o programa assim:
``` bash
./examples/BinarySearch
```

Você deverá ver uma lista de nomes e seus números em uma lista. Pesquise Emily digitando 6 no prompt e clique em `Enter`.

|![Procurando por Amy](../resources/debugging_process_searching_for_amy.svg)|
|:--:|
|Procurando por Amy.|

Agora execute o programa novamente e procure o número de `Ramona`. O programa é interrompido com uma mensagem `Falha de segmentação (core dumped)`! 😮

Ao se deparar com tal problema, você deve se perguntar: qual é o comportamento do bug?
Erros de falha de segmentação geralmente são um sinal de um dos seguintes problemas:
- Acessando um array fora dos limites.
- Desreferenciando um ponteiro NULL.
- Sobrecarga de memória/pilha.

Para obter mais informações, consulte [Lista de motivos comuns para falhas de segmentação em C](https://www.tutorialspoint.com/List-of-Common-Reasons-for-Segmentation-Faults-in-C-Cplusplus#:~:text=List%20of%20Common%20Reasons%20for%20Segmentation%20Faults%20in,7%20Stack%20overflow%208%20Writing%20to%20read-only%20memory).

Vamos dar uma olhada no código que implementa a busca binária em nosso código:

1. A função `binary_search()` leva três argumentos: o array de elementos, o comprimento do array e o número que estamos procurando. Em seguida, ele chama a função recursiva `rbin_search()`.

2. `rbin_search()` executa a pesquisa binária recursivamente e retorna o índice do elemento se encontrado. Caso contrário, ele retorna `-1`.

Uma `função recursiva` divide um problema em vários problemas menores chamando a si mesma e torna o problema mais fácil de lidar com um conjunto de **casos base**. Uma função recursiva que não termina geralmente apresenta problemas em um dos seguintes:

1. Os casos básicos estão incompletos.
2. As chamadas recursivas estão configuradas de maneira errada.

Vamos fazer alguma depuração!

## Usando declarações de impressão

Colocar declarações `print` em seu código é uma maneira suja, mas às vezes eficaz, de saber se seu código está funcionando conforme o esperado. Vá em frente e verifique se `rbin_search()` está funcionando corretamente, colocando declarações de impressão para ver a mudança dos valores.

{{% expand "***Dica 1: O que torna o problema que estamos tentando resolver menor?***" %}}
- Tente colocar a declaração print após a variável `middle` na função `rbinary_search` para ver como os valores `lo`, `hi` e `middle` estão mudando. Vá em frente e pesquise algo.
|![Colocando a declaração print para verificar os valores "lo", "hi" e "middle".](../resources/debugging_process_print_statement.svg)|
|:--:|
|Colocando declaração print para verificar valores `lo`, `hi` e `middle`.|

- Fique de olho nos valores conforme eles são impressos.
{{% /expand %}}

{{% expand "**Clique para mostrar a resposta**" %}}
A chamada recursiva para pesquisar na metade inferior do array está pesquisando na metade superior.

Para consertar isso, os argumentos `lo` e `hi` de `rbin_search()` precisam ser `lo` e `middle-1`.

|![Corrigindo chamada recursiva da metade inferior.](../resources/debugging_process_fixing_lowerhalf_search.svg)|
|:--:|
|Corrigindo chamada recursiva da metade inferior.|

|![Olhando como as variáveis "lo", "hi" e "middle" mudam ao procurar por Becky.](../resources/debugging_process_searching_for_becky.svg)|
|:--:|
|Observando como as variáveis "lo", "hi" e "middle" mudam ao procurar por Becky.|

Execute seu programa novamente e procure por um número abaixo de `5` e veja como `lo`, `hi` e `index` mudam.
{{% /expand %}}
<br/>

As declarações impressas não são a melhor ferramenta para usar quando a complexidade do programa aumenta. Eles são extremamente ineficientes e se um programador esquecer de removê-los, outra pessoa (por exemplo, um usuário executando o seu programa) poderá ver as declarações de impressão. Use declarações de impressão apenas em seções isoladas do seu código e **SEMPRE** lembre-se de removê-las 🙂.
Loading