GDB

Y ya para hablar esta temporada os voy a presentar GDB, que es una herramienta que sirve para depurar programas. La depuración es un proceso que nos permite mirar paso a paso la ejecución de un programa para encontrar y corregir posibles errores que hayamos cometido durante su desarrollo. GDB nos permite depurar de forma integral un programa, ejecutándolo paso a paso, haciendo un seguimiento a ciertas invocaciones, o comprobando el valor de algunas variables.

Depurar programas en C con GDB es una habilidad fundamental para cualquier programador que quiera entender a fondo el comportamiento de su código y solucionar errores complejos. Cuando nuestro programa no funciona como esperamos, la depuración nos permite inspeccionar paso a paso qué está ocurriendo internamente y corregir esos fallos que a veces se nos escapan con técnicas más básicas como el uso de printf.

Para empezar a depurar con GDB, lo primero que debemos hacer es compilar nuestro programa con la opción -g. Esta opción le indica al compilador GCC que incluya información adicional en el ejecutable, como los nombres de las variables y metadatos que GDB utilizará para mostrarnos el estado del programa de forma clara y precisa. Aunque esto hace que el archivo resultante sea un poco más grande, es imprescindible para poder aprovechar todas las funcionalidades del depurador.

Una vez compilado con -g, podemos lanzar GDB pasando el ejecutable como argumento, por ejemplo:

gdb prueba

Esto nos abre una interfaz de texto donde podemos introducir comandos para controlar la ejecución del programa. Uno de los comandos más útiles para comenzar es list, que nos muestra el código fuente del programa, permitiéndonos ver en qué línea estamos trabajando. Podemos usarlo sin argumentos para ver las primeras líneas, o pasarle un número o el nombre de una función para situarnos en un punto concreto, por ejemplo:

list 1
list main

Para ejecutar el programa dentro de GDB, usamos el comando run. Esto inicia la ejecución normal, pero con la ventaja de que podemos controlar el flujo y detenernos en puntos específicos.

Aquí es donde entran en juego los breakpoints o puntos de ruptura. Un breakpoint es una instrucción que le damos a GDB para que detenga la ejecución del programa cuando llegue a una línea o función determinada. Por ejemplo, si queremos que el programa se detenga al entrar en la función leerNumero, escribimos:

break leerNumero

Cuando ejecutamos run después de poner este breakpoint, el programa se ejecutará hasta que llegue a esa función y se pausará, devolviéndonos el control en la consola de GDB. En ese momento, podemos inspeccionar el estado del programa, ver los valores de las variables y decidir cómo continuar.

Para avanzar en la ejecución, GDB nos ofrece dos comandos muy importantes: next y step. El comando next ejecuta la siguiente línea de código sin entrar en funciones llamadas, mientras que step entra dentro de las funciones para que podamos depurar línea a línea dentro de ellas. Por ejemplo, si estamos en una llamada a leerNumero y queremos ver qué ocurre dentro, usamos step. Si solo queremos avanzar sin entrar en detalles, usamos next.

Además, si queremos repetir el último comando, simplemente pulsamos Enter sin escribir nada, lo que agiliza la depuración.

Para inspeccionar variables y expresiones, el comando print es esencial. Podemos ver el valor de una variable con:

print n

Y también evaluar expresiones más complejas, como multiplicaciones o casteos:

print 5 * n + 5
print (char) n

Si queremos saber el tipo de una variable, usamos ptype seguido del nombre de la variable:

ptype n

Cuando queremos que el programa continúe su ejecución hasta el siguiente breakpoint o hasta que termine, usamos continue. Si ya no necesitamos un breakpoint, podemos eliminarlo con clear seguido del nombre de la función o la línea donde está puesto:

clear leerNumero

Para comenzar la depuración justo en la función main, podemos usar el comando start main, que pone un breakpoint temporal en main y ejecuta el programa hasta ese punto.

Durante la depuración, si nos hemos metido dentro de varias funciones y queremos saber en qué punto del programa estamos, el comando where nos muestra la pila de llamadas o stack trace, indicándonos el camino que ha seguido la ejecución hasta el momento. Podemos navegar por los distintos marcos de la pila con up y down, para ver variables locales en diferentes contextos.

Si nos hemos metido dentro de una función y queremos salir rápidamente hasta que termine, sin ir línea a línea, usamos finish, que ejecuta el resto de la función y nos devuelve al marco anterior.

Una de las situaciones más comunes donde GDB es especialmente útil es cuando nuestro programa falla con un error como un segmentation fault. En estos casos, ejecutar el programa dentro de GDB nos permite ver exactamente en qué línea ocurrió el fallo y qué valores tenían las variables en ese momento, facilitando enormemente la identificación del problema. Por ejemplo, si intentamos dividir por cero, GDB nos mostrará el mensaje de error y podremos inspeccionar las variables para entender qué pasó.

En definitiva, GDB nos proporciona un entorno potente para controlar la ejecución de nuestros programas en C, permitiéndonos poner breakpoints, avanzar paso a paso, inspeccionar variables y entender errores complejos. Con práctica, estas herramientas se convierten en aliadas indispensables para mejorar nuestra productividad y calidad de código.

Lista de reproducción
  1. 1
    Instalar CodeBlocks
    15 minutos
  2. 2
    Funciones y hola mundo
    17 minutos
  3. 3
    Variables y tipos de datos
    17 minutos
  4. 4
    Condicionales y operadores lógicos
    16 minutos
  5. 5
    Bucles
    11 minutos
  6. 6
    Punteros
    12 minutos
  7. 7
    Arrays
    14 minutos
  8. 8
    Estructuras
    12 minutos
  9. 9
    Otras construcciones de C
    9 minutos
  10. 10
    Memoria dinámica
    8 minutos
  11. 11
    El preprocesador (parte 1)
    10 minutos
  12. 12
    El preprocesador (parte 2)
    9 minutos
  13. 13
    Archivos de cabecera y múltiples .c (parte 1)
    8 minutos
  14. 14
    Archivos de cabecera y múltiples .c (parte 2)
    9 minutos
  15. 15
    C desde la línea de comandos (parte 1)
    8 minutos
  16. 16
    C desde la línea de comandos (parte 2)
    9 minutos
  17. 17
    Break y continue
    10 minutos
  18. 18
    Goto
    13 minutos
  19. 19
    Manipulación de bits
    15 minutos
  20. 20
    Máscaras de bit
    19 minutos
  21. 21
    Archivos (1): fopen y fclose
    13 minutos
  22. 22
    Archivos (2): leer con fgetc
    9 minutos
  23. 23
    Archivos (3): fseek y ftell
    11 minutos
  24. 24
    Archivos (4): leer con fgets
    9 minutos
  25. 25
    Archivos (5): fputc y fputs
    7 minutos
  26. 26
    Archivos (6): volcar en archivos con fwrite
    10 minutos
  27. 27
    Archivos (7): fread, fwrite y los arrays
    10 minutos
  28. 28
    Archivos (8): entrada estándar y salida estándar
    9 minutos
  29. 29
    Archivos (9): buffers
    14 minutos
  30. 30
    Archivos (y 10): otras funciones útiles con archivos
    5 minutos
  31. 31
    printf (1)
    18 minutos
  32. 32
    printf (parte 2)
    12 minutos
  33. 33
    scanf (parte 1)
    17 minutos
  34. 34
    scanf (parte 2)
    17 minutos
  35. 35
    fprintf, sprintf y snprintf
    8 minutos
  36. 36
    Tipos de datos opacos
    13 minutos
  37. 37
    Bibliotecas estáticas
    13 minutos
  38. 38
    Bibliotecas dinámicas
    15 minutos
  39. 39
    Más flags: i mayúscula (include), wall, werror, pedantic...
    12 minutos
  40. 40
    pkg-config
    12 minutos
  41. 41
    Make
    17 minutos
  42. 42
    GDB
    21 minutos
  43. 43
    Variables globales
    6 minutos
  44. 44
    extern
    9 minutos
  45. 45
    Funciones variádicas
    12 minutos
  46. 46
    El optimizador de GCC y la opción -O
    12 minutos
  47. 47
    Volatile
    6 minutos