introducao_depuracao_codigos.html 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. <!--
  2. Introdução às técnicas básicas de depuração de código
  3. -->
  4. <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
  5. <meta name='keywords' content='mac0122, material, professores, leonidas de oliveira brandao'>
  6. <link rel='stylesheet' type='text/css' href='css_img_js_conf/all.css'>
  7. <link rel='stylesheet' type='text/css' href='css_img_js_conf/line_introducao_programacao.css'>
  8. <div class="pagina">
  9. <p class="secao">Por que devemos saber depurar o código?</p>
  10. <p>
  11. Se você já está envolvido com o aprendizado de <i>programação</i> há algum tempo, então muito provavelmente já recebeu
  12. <font color="#aa0000">mensagens de erro</font>, algumas <b>sintáticas</b> (quando existe algum erro na <i>sintaxe</i> de seu código)
  13. mas outras, geralmente mais difíceis de resolver, na <i>lógica da execução</i>, neste caso um erro <b>semântico</b> (o programa não faz
  14. o que era esperado que ele fizesse).
  15. </p>
  16. <p>
  17. Outra situação desagradável quanto é utilizado um ambiente para teste automático do código (caso é o caso do uso do
  18. <i>Moodle</i> com <i>VPL</i> ou com o <i>iTarefa/iVProg</i>), é o aprendiz conseguir (aparentemente) rodar com sucesso em seu ambiente,
  19. mas ao passar para o ambiente de seu curso, recebe alguma mensagem de erro...
  20. </p>
  21. <p>
  22. Então o que você faz? Olha todas as linhas do código e nada, tudo parece estar como deveria.
  23. O código parece correto, "não tem motivo algum para a saída ser diferente da esperada, certamente o
  24. <font color="#aa0000">sistema do curso tem erro</font>" é o que você pensa.
  25. <!--O avaliador automático deve estar com problema.-->
  26. </p>
  27. <p>
  28. Esta é uma reação natural, porém
  29. <font color="#000088">nem sempre "lemos" o que está codificado, mas o que pensamos ter codificado</font>,
  30. talvez você já tenha passado por isso ao escrever um redação...
  31. Nestes casos, o problema é nao darmos a devida atenção para todos os detalhes, mas em códigos mais complexos, eventualmente não
  32. examinamos algumas situações que os <i>casos-de-teste</i> estão preparados para testar.
  33. </p>
  34. <p>
  35. Portanto, seja por falta de atenção, paciência ou complexidade do código, erros são comuns.
  36. Então o que devemos fazer para achar e corrigir estes erros?
  37. </p>
  38. <p>
  39. Primeiro devemos entender a natureza do erro, existem duas grandes categorias,
  40. <font color="#008800"<i>erro sintático</i></font> ou <font color="#008800"<i>erro semântico</i></font>.
  41. </p>
  42. <ul>
  43. <li><b>Erro Sintático</b>:
  44. É um erro na sintaxe da linguagem, em sua gramática, como por exemplo, usar letra maiúscula inicial para o <i>comando de seleção</i>
  45. (o sistema não reconhecerá como "comando de seleção", tentará como uma "atribuição", mas como "atribuição" também não estará correta).
  46. Neste caso, seu programa <font color="#aa0000">não</font> poderá ser "rodado".
  47. <!--de escrita, em geral o próprio compilador avisa, seja como <i>erro</i> (seu programa não irá compilar) ou <i>avisos</i> (um problema "corrigivel" pelo compilador)-->
  48. </li>
  49. <li><b>Erro semântico</b>:
  50. É um erro na "lógica de seu código", em sua <i>semântica</i>, o código está sintaticamente correto, porém não faz o que se esperava dele.
  51. Por isso, este tipo de erro é geralmente mais difícil de ser identificado e corrigido.
  52. Para isso algumas técnicas básicas, como as apresentadas a seguir, podem ser de grande valia.
  53. <!-- Como o código está escrito seguindo a sintaxe correta, o compilador não acusa erro. -->
  54. </li>
  55. </ul>
  56. <p class="secao">Como tratar erros sintáticos?</p>
  57. <p>
  58. Como estes erros são acusados pelo próprio compilador são mais fáceis de identificar. Em geral, a mensagem de erro gerada indica a linha
  59. onde ocorreu o erro. Então é preciso calma e uma leitura cuidadosa para entender o significado da mensagem.
  60. </p>
  61. <p class="subsecao">1. Leia as mensagens de erro geradas pelo compilador/interpretador</p>
  62. <p>
  63. As mensagens de erro indicam qual o problema encontrado e porque a compilação/execução falhou. Por exemplo, considere os
  64. códigos <i>C</i> e <i>Python</i> a seguir:
  65. </p>
  66. <center>
  67. <i>Tab. 1. Exemplos de códigos com erros sintáticos (nome errado para o comando de impressão).</i>
  68. <table ><tr style="border-bottom: 1px solid #000;"><td style="border-right: 1px solid #000;"><i>C</i></td><td>&nbsp; <i>Python</i></td></tr>
  69. <tr><td style="border-right: 1px solid #000;">
  70. <pre>#include &lt;stdio.h&gt;
  71. int main (void) {
  72. <font color="#aa0000">prit</font>("Hello World\n");
  73. }</pre>
  74. <p>
  75. A mensagem de erro (ou advertência) recebida ao tentar compilar:
  76. </p>
  77. <pre style="font-size:0.8em">main.c:2:1: warning: implicit declaration of function ‘<font color="#aa0000">prit</font>’ [-Wimplicit-function-declaration]
  78. main.c: undefined reference to `prit'
  79. </pre></td><td><pre> def main () :
  80. <font color="#aa0000">prit</font>("Hello World");
  81. main();</pre>
  82. <p>
  83. &nbsp; A mensagem de erro recebida:
  84. <pre style="font-size:0.8em"> Traceback (most recent call last):
  85. File "teste2.py", line 3, in <module>
  86. main();
  87. File "teste2.py", line 2, in main
  88. <font color="#aa0000">prit</font>("teste");
  89. NameError: global name 'prit' is not defined</pre></td></tr></table>
  90. </center>
  91. <p>
  92. Examinando as mensagens recebidas, podemos perceber que a linha com a "tentativa" de comando <tt><font color="#aa0000">prit</font></tt>
  93. está com problema.
  94. Se prestarmos atenção, nesta linha está escrito <tt><font color="#aa0000">prit</font></tt> ao invés de
  95. <tt><font color="#008800">printf</font></tt> para a linguagem <i>C</i>
  96. e <tt><font color="#008800">print</font></tt> para a linguagem <i>Python</i>.
  97. </p>
  98. <p>
  99. Infelizmente nem sempre os erros são tão claros e simples como este. Como programação também é uma tarefa de exploração, as vezes
  100. utilizamos ferramentas que não estamos habituados, portanto apenas a leitura do erro não é suficiente.
  101. </p>
  102. <p class="subsecao">2. Procurar na <i>Internet</i></p>
  103. <p>
  104. Provavelmente você não é a primeira pessoa a receber esta mensagem de erro, nem será a última, então, se fizer uma busca pela
  105. <i>Web</i>, provavelmente encontrará uma mensagem relatando erro semelhante e poderá estudar as proposta de correção.
  106. <!-- com uma busca rápida em fóruns, você conseguirá achar outra pessoa que passou por esta mesma situação e como ela solucionou. -->
  107. </p>
  108. <p>
  109. Em geral basta copiar o <i>mensagem de erro</i> e colar na barra de pesquisa do seu "buscador" preferido
  110. (experimente o <a href="https://duckduckgo.org/?q=%22debugging+program%22+%22find+out%22+error&t=h_&ia=web"
  111. title="Um buscador que tem por princípio NAO persegui-lo! Não vender seus dados...">duckduckgo.org</a>).
  112. </p>
  113. <!--
  114. teste_c/usar_pacote_assert.c
  115. http://www.cs.yale.edu/homes/aspnes/pinewiki/C(2f)Debugging.html
  116. -->
  117. <p class="secao">Como tratar erros semânticos?</p>
  118. <p>
  119. Como são erros de "lógica de programação", <!-- ou seja, seu programa executa, --> então existe ao menos um <i>conjunto de entradas</i>
  120. para as quais a saída não é a esperada.
  121. Isto nos deixa em uma situação que requer mais atenção e nem sempre será uma solução fácil.
  122. Muitas vezes requerem reescrita parcial ou total do código.
  123. </p>
  124. <p class="subsecao">1. Procure <i>simular</i> seu código</p>
  125. <p>
  126. Se o seu algoritmo não é muito grande, pode-se fazer uma simulação dele para entender exatamente o que está fazendo e com isso identificar
  127. o momento que o primeiro erro aparece.
  128. </p>
  129. <p>
  130. Para simular, construa uma tabela com todas variáveis de seu código (ou apenas aquelas que deseja rastrear),
  131. sendo que cada variável terá sua coluna.
  132. A cada instrução que altere o valor de determinada variáveis, deve-se registrar o novo valor na coluna correspondente,
  133. na linha seguinte à última linha que teve um valor registrado, ou seja, as linhas da tabela indicam a ordem de execução
  134. (uma entrada mais acima indica que a instrução que alterou a variável correspondente ocorreu "mais cedo").
  135. </p>
  136. <p>
  137. <b>ATENÇÃO</b>, é essencial seguir precisamente a ordem de execução dos comandos e deve-se simular/executar
  138. <b>exatamente</b> o que está redigido (e não como "acha que deveria estar")!
  139. </p>
  140. <center>
  141. <p>
  142. <i>Tab. 2. Exemplo de simulação de um código (sem erro): imprimir a somas dos naturais</i>. Usando "pseudo-código".</i>
  143. <table><tr><td><pre style="font-size:0.8em">
  144. # Codigo | N | soma | i | Impressoes | Explicacoes (por linha)
  145. --------------------------------------------+-----------------------+------------+--------------------------
  146. 1 variaveis N, soma=0, i=0; | ? | 0 | 0 | | 1 : valores iniciais (N desconhecido!)
  147. 2 N = leia(); // ler valor e guardar em N | 3 | | | | 2 : ler valor e guardar em N (supor 3)
  148. 3 enquanto (i &lt; N) { | | | | | 3 : 0 &lt; 3 verdadeiro =&gt; entra no laco
  149. 4 soma = soma + i; | | 0 | | | 4 : acumular i em soma
  150. 5 i = i + 1; | | | 1 | | 5 : acumular 1 em i
  151. 6 } | | | | | 6 : final laco, voltar 'a linha 3
  152. 7 escreva("fim"); | | | | | 3 : 1 &lt; 3 verdadeiro =&gt; entra no laco
  153. | | 1 | | | 4 : acumular i em soma
  154. | | | 2 | | 5 : acumular 1 em i
  155. | | | | | 6 : final laco, voltar 'a linha 3
  156. | | | | | 3 : 2 &lt; 3 verdadeiro =&gt; entra no laco
  157. | | 3 | | | 4 : acumular i em soma
  158. | | | 3 | | 5 : acumular 1 em i
  159. | | | | | 6 : final laco, voltar 'a linha 3
  160. | | | | | 3 : 3 &lt; 3 falso =&gt; vai para final do laco
  161. | | | | 3 | 7 : escrever valor em soma</pre>
  162. </td></tr></table></p>
  163. </center>
  164. <p class="subsecao">2. Releia o código</p>
  165. <p>
  166. Eventualmente existe um erro de lógica que "salte aos olhos", podendo deste modo encontrá-lo rapidamente.
  167. Vejamos dois casos clássicos de erros semânticos (e que portanto podem rapidamente ser identificados), um
  168. na linguagem <i>C</i> e outro na linguagem <i>Python</i>.
  169. </p>
  170. <center>
  171. <i>Tab. 3. Um exemplo de erro semânticos clássico em <i>C</i> e um em <i>Python</i>.</i>
  172. <br/>
  173. <table ><tr style="border-bottom: 1px solid #000;"><td style="border-right: 1px solid #000;">Erro semântico "clássico" em <i>C</i></td>
  174. <td>&nbsp;</td>
  175. <td>Erro semântico "clássico" em <i>Python</i></td></tr><tr><td style="border-right: 1px solid #000;"></td></tr>
  176. <tr><td style="border-right: 1px solid #000;">
  177. <pre>if (x = 0) // supondo que x chegue aqui com valor 0
  178. printf("O valor e' nulo\n");</pre>
  179. <p>
  180. Entretanto, a mensagem <b>não</b> é impressa!
  181. A razão é que em <i>C</i> uma <i>expressão lógica</i> é uma <i>expressão aritmética</i> que é comparada com o valor nulo,
  182. se for zero, então o resultado é <b>falso</b>, senão é <b>verdadeiro</b>.
  183. Como a <i>expressão lógica</i> que aparece é <tt>x = 0</tt>, na verdade é um <i>comando de atribuição</i>,
  184. significando que a variável <tt>x</tt> receberá (novamente) o valor nulo
  185. e o resultado da <i>expressão aritmética</i> é precisamente o zero!
  186. Ou seja, o resultado lógico será <i>falso</i> e o comando subordinado <b>não</b> será impresso!<br/>
  187. Assim, correção é simples:
  188. </p>
  189. <p>
  190. <pre>if (x == 0) // correcao: usar "=="
  191. printf("O valor e' nulo\n");</pre></td>
  192. <td>&nbsp;</td>
  193. <td>
  194. <pre>if (x==0) : # supondo que x chegue aqui com valor 1
  195. y = 2;
  196. print("O valor e' nulo");</pre>
  197. </p>
  198. </center>
  199. <p>
  200. Entretanto, a mensagem é <b>incorretamente</b> impressa!
  201. A razão é que em <i>Python</i> a <i>subordinação</i> de comandos é definida por <i>indentações</i>, ou seja,
  202. para que o <tt>print("O valor e' nulo")</tt> estivesse corretamente subordinado ao comando <tt>if</tt> ele precisaria estar
  203. alinhado ao comando <tt>y = 2;</tt>.<br/>
  204. Assim, correção é simples:
  205. <pre> if (x == 0) :
  206. y = 2;
  207. print("O valor e' nulo"); # correcao, alinhar com a linha acima</pre></td></tr></table>
  208. </center>
  209. <p>
  210. Porém, nem sempre é realista reler o código todo, então foque em partes críticas,
  211. <!--
  212. Partes críticas são limites entre
  213. trechos de código, portanto devemos ter bastante atenção para <b>seleção</b> (if), <b>condicional de laços</b>
  214. (while, for, do while), <b>atribuições</b> e <b>chamadas de função</b>.
  215. -->
  216. aquelas partes em que o erro acontece. Geralmente o erro encontra-se em algum comando de <b>seleção</b> (<tt>if</tt>),
  217. em <b>condicional de laço</b> (<tt>while</tt>, <tt>for</tt> ou outros), em <b>atribuições</b> ou em <b>chamadas de função</b>.
  218. </p>
  219. <p>
  220. Este método requer maior conhecimento do programador para saber qual a parte crítica do código, elas podem ser diferentes para cada problema.
  221. Além disto é necessário um entendimento maior do problema.
  222. <!-- , conseguindo identificar a quais comandos são necessários e em qual ordem devem ser executados. -->
  223. Então a técnica seguinte pode ajudar.
  224. </p>
  225. <a name="bandeiras">
  226. <p class="subsecao">3. Utilizar "bandeiras" (flags) </p>
  227. </a>
  228. <p>
  229. Nem sempre é possível identificar tudo apenas relendo o código, principalmente em códigos mais complexos. Portanto precisamos de
  230. mais ferramentas. Esta técnica ajuda a encontrar as <i>partes críticas</i> de seu código e consiste em imprimir algumas variáveis
  231. ao longo do código.
  232. </p>
  233. <p>
  234. Se você não tem a mínima ideia de onde o erro esteja, pode colocar uma impressão de uma lista de variáveis como primeira instrução
  235. dentro de cada comando de <i>seleção</i> ou em cada comando de <i>repetição</i>.
  236. No caso de laço infinito (um erro muito comum!), o uso de uma "bandeira" como primeira instrução do <i>comando de repetição</i>
  237. lhe indicará claramente o problema, <i>laço infinito</i>!
  238. <!--
  239. Esta técnica consiste em imprimir os valores de variáveis em um momento importante, em geral, logo antes dela ser utilizada.
  240. Através dessa impressão podemos comparar nossa expectativa, qual o valor que a variável deveria
  241. ter, com a realidade, qual valor ela tem.
  242. -->
  243. </p>
  244. <!--
  245. <p>
  246. Desta forma devemos criar casos de teste nos quais saibamos os valores que as variáveis devem assumir durante a execução do código.
  247. </p>
  248. -->
  249. <p>
  250. Considere o seguinte problema: <i>Escreva um programa que imprima a frase "hello world" 10 vezes</i>.
  251. Suponha que um colega tenha apresentado a seguinte solução (em <i>C</i> à esquerda e em <i>Python</i> à direita).
  252. </p>
  253. <center>
  254. <i>Tab. 4. Um exemplo de erro semântico clássico: laço infinito.</i>
  255. <table ><tr style="border-bottom: 1px solid #000;"><td style="border-right: 1px solid #000;"><i>C</i></td><td>&nbsp; <i>Python</i></td></tr>
  256. <tr><td style="border-right: 1px solid #000;">
  257. <pre>#include &lt;stdio.h&gt;
  258. int main (void) {
  259. int i=0;
  260. while (i &lt; 10) {
  261. printf("hello world\n");
  262. }
  263. }</pre>
  264. </td><td><pre> def main () :
  265. i = 0;
  266. while (i &lt; 10) :
  267. print("Hello World");</pre>
  268. </td></tr></table>
  269. </center>
  270. <p>
  271. Ao executar o código, a frase fica sendo impressa indefinidamente (portanto <i>laço infinito</i>), neste caso, qual o melhor local para inserirmos uma bandeira?
  272. </p>
  273. <p>
  274. Como o problema está relacionado à frase ser impressa muitas vezes, fica natural colocar a <i>bandeira</i> como primeira instrução do <i>comando de repetição</i>.
  275. </p>
  276. <p style="color:#00aa00">
  277. Mas qual variável imprimir?
  278. </p>
  279. <p>
  280. No exemplo acima (tab. 4), não é difícil deduzir, pois o laço usa a variável <tt>i</tt> como controle (e não tem outra...).
  281. </p>
  282. <p>
  283. Ao fazer isto e executarmos o código percebemos que além da frase, o valor de <tt>i</tt> está sempre com o valor nulo.
  284. Portanto o que falta é o incremento na variável de controlo, antes de testar a <i>condição de entrada</i>.
  285. <br/>
  286. Ou seja, identificamos que não existe uma atribuição para incrementar a variável de controle.
  287. </p>
  288. <p style="color:#00aa00">
  289. O que fazer em códigos maiores?
  290. </p>
  291. <p>
  292. Quando seu código for grande, será necessário identificar cada "bandeira", por exemplo, usando a linguagem "Portugol",
  293. use algo parecido com:
  294. <tt>imprima("1: alguma informacao daqui");... imprima("2: algo daqui");...imprima("10: algo daqui");...</tt>.
  295. </p>
  296. <p>
  297. Mas vale a pena fazer uma análise geral de seu código e da resposta obtida, isso pode indicar um provável local de erro,
  298. neste caso, concentre-se neste trecho, colocando uma "bandeira" em cada comando de <i>seleção</i> e em todos os inícios
  299. de <i>laços</i>.
  300. Neste caso vale a pena diferenciar as mensagens, por exemplo, com:
  301. <tt>imprima("se 1: i=%d", i); ... imprima("laco 1: j=%d", j); ... </tt>.
  302. </p>
  303. <p>
  304. Esta técnica é muito utilizada por ser rápida e extremamente efetiva.
  305. Mas lembre-se: sempre apague/comente as "bandeiras" depois de utilizá-las,
  306. você não quer que a execução do seu código final fique poluída com a impressão de várias "bandeiras".
  307. </p>
  308. <p class="subsecao">4. Explique o código para alguém:</p>
  309. <p>
  310. Esta técnica é conhecida como
  311. "<a href="https://rubberduckdebugging.com/" title="mais detalhes sobre RDD">Rubber duck debugging" (RDD - "depuração pato de borracha")</a>,
  312. que está associada a ideias bastante antigas
  313. (como "tente ensinar para aprender") e outras nem tanto, como a técnica "pensamento em voz alta" (<i>think aloud</i>).
  314. A RDD consiste em explicar, linha por linha, o código para uma outra pessoa, ou na falta de uma pessoa explique para um objeto
  315. (como o "pato de borracha").
  316. </p>
  317. <p>
  318. Pode-se adotar uma abordagem hierárquica, procurando explicar de modo mais geral o que seu programa deveria fazer e depois ir detalhando.
  319. Primeiro explique os objetivos do problema, o que você deseja fazer e quais ideias tem para resolvê-lo. Eventualmente pode haver um
  320. problema de entendimento de enunciado.
  321. <br/>
  322. Se o problema estiver nos detalhes, tente explicar cada trecho de seu código e, em cada um, o que que cada linha dele faz.
  323. </p>
  324. <p>
  325. Ao explicar o trabalho para outra pessoa que não tem a mesma familiaridade com o problema ou com seu código,
  326. eventualmente você poderá compreender melhor o fez ou o que deveria fazer.
  327. <!-- acaba entrando em detalhes que pareciam óbvios ou irrelevantes, sendo por vezes a causa da dor de cabeça.-->
  328. </p>
  329. <p class="autoria">
  330. <a href="https://www.ime.usp.br/~leo" target="_blank" title="seguir para a pagina do prof. Leônidas">Leônidas de Oliveira Brandão</a><br/>
  331. <a href="http://www.ime.usp.br/~leo" target="_blank" title="seguir para a página do LInE">http://line.ime.usp.br</a>
  332. </p>
  333. <p class="rodape">
  334. <b>Alterações</b>:<br/>
  335. 2020/08/15: novo formato, pequenas revisões<br/>
  336. 2020/08/07: Sexta, 07 Agosto 2020, 20:15<br/>
  337. 2020/03/30: Segunda, 30 Março 2020, 21:15
  338. </p>
  339. <!--
  340. Para entender melhor esta história clique aqui</a>.
  341. https://rubberduckdebugging.com/
  342. https://www.techopedia.com/definition/31880/rubber-duck-debugging
  343. http://lists.ethernal.org/oldarchives/cantlug-0211/msg00174.html
  344. -->
  345. </div>