// aturar el programa per mirar-hi dins

Depuració en C amb gdb

El compilador només detecta errors de sintaxi. Un error lògic (el programa compila i corre, però el resultat és incorrecte) cal caçar-lo en marxa — per això existeix gdb.

Compilar per depurar

gdb necessita que el compilador hi deixi informació extra (noms de variables, línies del codi font) amb l'opció -g.

gcc -g programa.c -o programa
gdb ./programa

Punts de ruptura

Un punt de ruptura (breakpoint) atura l'execució just abans d'una línia o funció concreta, perquè puguis inspeccionar l'estat del programa en aquell moment.

(gdb) break compta_elements
(gdb) run

break pot rebre un nom de funció o un número de línia. run comença l'execució, que s'aturarà automàticament quan hi arribi.

Avançar i inspeccionar

ComandaFa
next (n)executa la línia actual i avança a la següent, sense entrar dins de cap funció que es cridi
step (s)igual, però SÍ entra dins de la funció cridada
print x (p x)mostra el valor actual de la variable x
continue (c)reprèn l'execució fins al pròxim punt de ruptura, o fins que el programa acabi

Ho veuràs tot en marxa a la pestanya d'execució: una sessió real de gdb, aturant un programa amb un bug de debò per trobar-lo.

Taula de referència

ComandaFa
break <línia/funció>posa un punt de ruptura
runcomença l'execució
next / stepavança una línia (sense / amb entrar a funcions)
print <expressió>mostra un valor
listmostra el codi font al voltant de la línia actual
continuereprèn l'execució
quitsurt de gdb

Una sessió real de gdb, pas a pas

Aquestes dues sessions són transcripcions de gdb executant-se de debò — inclosos els valors exactes que va mostrar.

Sessions de gdb

Codi

Variables

NomValor

Sessió de gdb

Pas 1 / 1

Problemes per resoldre

Sense solució: compila cada programa amb gcc -g programa.c -o programa i fes servir gdb de debò per trobar el bug. Les pistes són plegades perquè les obris només si les necessites.

1. Un bucle que no avança

Aquest programa s'hauria de quedar penjat (bucle infinit):

int i = 0;
while (i < 5) {
    printf("%d\n", i);
}

Fes servir gdb per confirmar per què i mai arriba a 5.

Pistes

Posa un break a la línia del printf, fes run, i després diversos next seguits de print i cada cop.

Veuràs que i val sempre 0: li falta un i++; dins del bucle.

2. Un punt i coma de més

Aquesta funció hauria de calcular base elevat a exp (multiplicant resultat per base, exp vegades), però potencia(2, 5) dona 2 en lloc de 32:

int potencia(int base, int exp) {
    int i, resultat = 1;

    for (i = 0; i < exp; i++);
        resultat = resultat * base;

    return resultat;
}

Troba el bug amb gdb.

Pistes

Posa un break potencia, fes run i després next diverses vegades seguint la línia activa: veuràs que resultat només canvia un cop, encara que el bucle hauria de fer 5 voltes.

Mira bé el final de la línia del for: hi ha un punt i coma (;) que el tanca immediatament, deixant-lo sense cos. La línia següent (resultat = resultat * base;) ja no en forma part: s'executa només una vegada, després que el bucle hagi acabat. (De fet, gcc -Wall ja t'hauria avisat d'això amb un avís de "misleading indentation".)

3. Una variable sense inicialitzar

Aquest programa hauria de sumar 1, 2 i 3, però dona un resultat diferent cada cop que s'executa:

int total;
total = total + 1;
total = total + 2;
total = total + 3;
printf("%d\n", total);
Pistes

Posa un break a la primera línia i fes print total abans de res més. Ja veuràs que no val 0: mai s'ha inicialitzat, així que conté el que hi hagués abans en aquella posició de memòria.

Exercicis d'autoavaluació

Aquests es generen amb valors nous a l'atzar i tenen solució comprovable.

—

Un cop el programa s'atura al punt de ruptura, quin valor mostrarà aquest print?