Tutorial GDB completo en español: domina el depurador

Última actualización: 26/02/2026
Autor: Isaac
  • Compila siempre con -g o -ggdb para que GDB muestre líneas, variables y funciones de forma legible.
  • Usa breakpoints (simples y condicionales) junto con run, next, step y continue para controlar la ejecución.
  • Combina print, list, backtrace y watchpoints para inspeccionar variables, memoria y pila de llamadas.
  • Aprovecha atajos de teclado y comandos info para depurar más rápido programas complejos en C y C++.

Tutorial gdb en español

Si programas en C o C++ tarde o temprano te vas a topar con bugs que no se dejan cazar solo a base de printf y prueba‑error. En ese punto es cuando GDB deja de ser “ese comando raro” y se convierte en una herramienta imprescindible para entender qué está haciendo realmente tu programa línea a línea.

En esta guía completa vamos a ver cómo usar GDB con soltura: desde cómo compilar tu código con símbolos de depuración, hasta cómo colocar breakpoints, inspeccionar variables, moverte por la pila y aprovechar atajos de teclado. Todo desde la terminal, con ejemplos prácticos y explicaciones claras, para que puedas depurar como es debido y dejes de perseguir errores a ciegas.

Qué es GDB y por qué merece la pena usarlo

GDB (GNU Debugger) es el depurador estándar del ecosistema GNU y se utiliza principalmente con C y C++, aunque también soporta otros lenguajes. Funciona sobre ejecutables ya compilados y te permite “asomarte” al interior de tu programa mientras corre, ver qué pasa cuando se cuelga y manipular su ejecución.

A diferencia de depurar con mensajes por pantalla, GDB permite detener el programa en cualquier punto, examinar memoria, variables, stack, cambiar valores sobre la marcha y reanudar la ejecución. Todo esto se hace mediante una interfaz de línea de comandos muy veterana pero tremendamente potente.

GDB existe desde los inicios de Unix y, como otras herramientas clásicas tipo gcc o g++, nació para trabajar directamente en terminal, sin interfaz gráfica. Hoy hay frontends con ventanas y botones, pero una vez dominas los comandos básicos, la mayoría de desarrolladores termina siendo más rápido con GDB “a pelo”.

Un escenario típico donde GDB brilla es cuando tu programa explota con un temido Segmentation fault (SIGSEGV). Sin depurador solo ves que algo ha accedido a memoria inválida; con GDB puedes saber en qué línea ha petado, qué función estaba en curso y qué contenía cada variable importante en ese momento.

Compilar programas para depurarlos con GDB

Compilar y depurar con gdb

Para que GDB pueda trabajar a gusto, el ejecutable debe incluir información de depuración: nombres de variables, funciones, líneas de código fuente, etc. Eso se consigue añadiendo opciones concretas al compilador, como explica nuestro tutorial completo del comando gcc y sus opciones clave.

Lo mínimo es compilar con la opción -g. Por ejemplo, si tienes un archivo crash.c con un bug, puedes generar un binario llamado crash con:

gcc -g -o crash crash.c

La opción -g no cambia la lógica del programa, solo añade metadatos para que GDB pueda mostrarte el código fuente, las líneas exactas y los nombres reales de las variables en los stack frames. El ejecutable pesa un poco más, pero merece totalmente la pena.

En proyectos donde te interese usar el estándar C99 y seguir depurando cómodamente, puedes combinar opciones como:

gcc -std=c99 -g -o test test.c

Si usas C++ o quieres una integración más afinada con GDB, muchas guías recomiendan -ggdb, que genera información de depuración específica para este depurador:

g++ -ggdb -o programa main.cpp otras.cpp

Resumiendo: siempre que vayas a depurar un binario con GDB, asegúrate de haberlo compilado con -g o -ggdb; de lo contrario verás direcciones de memoria y símbolos crípticos en lugar de funciones y líneas legibles.

Primer contacto: iniciar GDB y ejecutar tu programa

Una vez tienes el ejecutable con símbolos, puedes arrancar GDB de dos formas: lanzando el depurador “vacío” o indicándole ya el binario que quieres analizar. La segunda es la que más se usa: arrancas y depuras el ejecutable en un único paso.

Para cargar y depurar, por ejemplo, un binario llamado crash:

gdb crash

Verás un encabezado con la versión de GDB, copyright y aviso de licencia, y luego el prompt típico:

(gdb)

Desde ese prompt puedes ejecutar tu programa con el comando run (o simplemente r):

(gdb) run
Starting program: ./crash

Si tu programa espera entrada estándar, GDB se la pasará tal cual. Si en lugar de indicar el ejecutable al inicio has lanzado solo gdb, puedes asociar un binario más tarde con el comando file:

  PC No Encuentra Chromecast | 15 Posibles Soluciones

(gdb) file memsim

Para salir de GDB basta con escribir quit o su abreviatura q. Si el programa se está ejecutando, GDB te pedirá confirmación para terminar la depuración.

Ejemplo práctico: cazando un Segmentation fault

Veamos un ejemplo clásico para entender el flujo de trabajo. Imagina un programa en C que pide un número, calcula la suma de 1 a n y muestra el resultado, pero contiene un fallo de memoria:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char *buf;

int sum_to_n(int num) {
    int i, sum = 0;
    for (i = 1; i <= num; i++)
        sum += i;
    return sum;
}

void printSum() {
    char line;
    printf("enter a number:\n");
    fgets(line, 10, stdin);
    if (line != NULL)
        strtok(line, "\n");
    sprintf(buf, "sum=%d", sum_to_n(atoi(line)));
    printf("%s\n", buf);
}

int main() {
    printSum();
    return 0;
}

Está claro que algo huele raro, pero si lo ejecutas tal cual basta con compilarlo y correrlo para ver el desastre:

gcc -g -o crash crash.c
./crash
enter a number:
5
Segmentation fault

El sistema solo avisa de un acceso inválido a memoria, nada más. Ahora, si arrancas el programa dentro de GDB puedes ver mucho más detalle. Carga el binario y ejecútalo:

gdb crash
(gdb) run
Starting program: ./crash
enter a number:
10

Cuando se produce el fallo, GDB te lo notifica con la señal correspondiente:

Program received signal SIGSEGV, Segmentation fault.
0x0017fa24 in _IO_str_overflow_internal () from /lib/tls/libc.so.6

Para saber qué te ha llevado hasta ahí, usas el comando backtrace (o bt):

(gdb) backtrace
#0 0x0017fa24 in _IO_str_overflow_internal() from /lib/tls/libc.so.6
#1 0x0017e4a8 in _IO_default_xsputn_internal() from /lib/tls/libc.so.6
#2 0x001554e7 in vfprintf() from /lib/tls/libc.so.6
#3 0x001733dc in vsprintf() from /lib/tls/libc.so.6
#4 0x0015e03d in sprintf() from /lib/tls/libc.so.6
#5 0x08048487 in printSum() at crash.c:22
#6 0x080484b7 in main() at crash.c:28

La traza muestra que main llamó a printSum, que a su vez llamó a sprintf, y a partir de ahí se encadenaron funciones internas de la libc hasta el crash. No podemos tocar la implementación de sprintf, pero sí ver qué parámetros le hemos pasado en la línea 22 de crash.c.

Breakpoints, ejecución paso a paso y comandos básicos

La gracia de un depurador es poder parar la ejecución donde te interesa. Para eso están los breakpoints o puntos de ruptura, que detienen el programa justo antes de ejecutar una línea o cuando se entra en una función.

En el ejemplo anterior, queremos ver qué pasa justo antes de llamar a sprintf. Podemos poner un breakpoint en la línea 22 de crash.c directamente desde GDB:

(gdb) break crash.c:22
Breakpoint 1 at 0x804845b: file crash.c, line 22.

Si reejecutas el programa con run, GDB te avisará de que ya estaba en marcha y te preguntará si quieres empezar de cero. Dile que sí y llega al punto de ruptura:

(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: ./crash
enter a number:
10
Breakpoint 1, printSum() at crash.c:22
22 sprintf(buf, "sum=%d", sum_to_n(atoi(line)));

Con el programa parado ahí, podemos inspeccionar variables con el comando print (o p):

(gdb) print line
$1 = "10\000\000\000\000\000\000…"

La cadena leída parece razonable. Ahora miramos el puntero buf:

(gdb) print buf
$2 = 0x0

Ahí está el bicho: estamos pasando a sprintf un puntero nulo como destino, es decir, intentando escribir en una dirección inválida. La solución es reservar memoria para buf o usar un array local en lugar de un puntero sin inicializar.

GDB también permite ejecutar línea a línea gracias a los comandos next (n), step (s) y until (u):

  • next (n): ejecuta la siguiente línea, pero si hay una llamada a función, la ejecuta de golpe sin entrar en ella.
  • step (s): ejecuta la línea y, si hay función llamada, entra en su primera instrucción, ideal para seguir funciones propias.
  • until (u): muy útil dentro de un bucle; continúa la ejecución hasta salir de ese bucle y alcanzar la línea indicada o la siguiente fuera del ciclo.

Por ejemplo, si pones un breakpoint en la línea del bucle de sum_to_n y luego usas n o u, puedes ver cómo evoluciona el acumulador sin tener que iterar manualmente todas las vueltas:

(gdb) break crash.c:10
(gdb) run
Breakpoint 1, sum_to_n (num=50) at crash.c:10
10 for (i = 1; i <= num; i++)
(gdb) n
11 sum += i;
(gdb) n
10 for (i = 1; i <= num; i++)
(gdb) until 12
12 return sum;

Cuando quieres que el programa siga corriendo hasta el próximo breakpoint o hasta que acabe, usas continue (o c):

  Los 7 Mejores Programas Para Grabar Música.

(gdb) continue
Continuing.

Breakpoints condicionales y gestión de puntos de parada

En programas con bucles grandes o muchas iteraciones, no siempre quieres parar “siempre en el mismo sitio”. Ahí entran en juego los breakpoints condicionales, que solo se activan si se cumple una expresión lógica.

Supón que quieres parar dentro de sum_to_n solo cuando el parámetro num sea 50. Primero creas el breakpoint en la línea deseada y luego le añades la condición:

(gdb) break crash.c:10
Breakpoint 1 at 0x8048441: file crash.c, line 10.
(gdb) condition 1 num == 50

Tras lanzar de nuevo el programa, GDB solo se detendrá cuando num valga 50 al llegar a esa línea:

(gdb) run
Starting program: ./crash
enter a number:
50
Breakpoint 1, sum_to_n (num=50) at crash.c:10
10 for (i = 1; i <= num; i++)

Para consultar los puntos de ruptura activos y sus números, puedes usar info breakpoints. Los breakpoints se pueden manipular con varios comandos:

  • delete: sin argumentos, borra todos los breakpoints; con número, elimina solo el indicado (delete 1).
  • clear: quita el breakpoint asociado a una función o línea (clear main, clear 42…).
  • disable / enable: permiten desactivar temporalmente un breakpoint y volverlo a activar más tarde.

Además de breakpoints clásicos también existen los watchpoints, que no paran en una línea concreta sino cuando se modifica o se lee el contenido de una variable. Se crean con watch variable (para cambios) o rwatch variable (para lecturas), y se listan con info watch. Son muy útiles para perseguir escrituras inesperadas sobre una estructura complicada.

Explorar código, variables y memoria

Con el programa detenido, lo que más harás será moverte por el código y mirar valores. GDB ofrece una colección de comandos bastante cómoda para eso, empezando por list, que muestra por pantalla fragmentos del fuente.

El uso más habitual de list es pedir unas cuantas líneas alrededor de un número de línea o de una función:

  • list 20: enseña unas líneas centradas en la línea 20.
  • list main: empieza a listar desde el principio de main.
  • list a secas: continúa listando a partir de la última línea mostrada.

Para inspeccionar valores, ya hemos visto que print es el comando estrella, pero no está limitado a simples variables. Puedes evaluar expresiones completas, hacer casts, acceder a campos de estructuras o mirar elementos de un array:

(gdb) print intermedio
$1 = 134513691
(gdb) print primero
$2 = 2654196
(gdb) print lista@25

En el ejemplo de un programa con variables sin inicializar, GDB permite comprobar rápidamente que no se ha dado un valor inicial a una variable local y que su contenido es basura. Esto explica por qué cada ejecución imprime un número distinto, algo típico cuando dependes de memoria automática sin inicializar.

Si necesitas ver el contenido de memoria cruda en una dirección, puedes combinar print &variable para obtener una dirección y luego usar el comando x (examine):

(gdb) print &num
$1 = (int *) 0xbffff580
(gdb) x 0xbffff580
0xbffff580: 0x00000064

Además, con ptype puedes pedirle a GDB que te indique el tipo de una variable o expresión, algo práctico cuando trabajas con estructuras complejas o plantillas en C++ y te lías con los tipos:

(gdb) ptype saldo_ptr

Otra función potente es la posibilidad de modificar variables en caliente con el comando set var. Por ejemplo, si quieres que un puntero llamado saldo_ptr pase a apuntar a saldo durante la depuración:

(gdb) set var saldo_ptr = &saldo

A partir de ese momento, la ejecución continúa con ese nuevo valor, lo que permite probar escenarios sin recompilar el programa cada vez.

Control fino de la ejecución y la pila de llamadas

Cuando te metes en programas grandes, no basta con avanzar línea a línea; necesitas saber por qué has llegado hasta ahí y moverte entre distintos contextos de llamada. GDB ofrece varios comandos para gestionar la pila (stack) y navegar por los distintos frames.

El comando where (o backtrace/bt) muestra la pila de llamadas, desde la función actual hasta el punto de entrada. Cada frame incluye la función, parámetros y archivo/linea. Puedes subir y bajar por esos frames con up y down para inspeccionar variables locales de distintos niveles:

  La guía definitiva sobre NirSoft Utilities: más de 250 herramientas gratuitas para mejorar Windows

(gdb) where
#0 funcion_profunda() at modulo.c:120
#1 calcula() at core.c:45
#2 main() at main.c:30

Para salir rápidamente de una función sin ir paso a paso hasta su final, existe el comando finish, que ejecuta el resto del cuerpo de la función actual y detiene la ejecución en el punto de llamada, devolviéndote al frame anterior.

Si quieres que la depuración comience directamente en main sin tener que pararte antes en código de inicio de la libc, hay un atajo muy útil: start main. Este comando arranca el programa y coloca un breakpoint temporal justo al entrar en la función main.

Para repetir el último comando introducido, basta con pulsar ENTER en una línea en blanco. Esto resulta muy cómodo cuando estás encadenando varios next o step seguidos.

Atajos de teclado y manejo del historial en GDB

Como GDB se usa desde la terminal, aprovecha las capacidades de edición de línea de GNU Readline. Eso significa que puedes usar un buen repertorio de atajos de teclado para moverte, editar comandos y reutilizar órdenes anteriores sin tener que escribirlo todo a mano.

Entre los atajos más útiles están:

  • CTRL+P / CTRL+N: navegar hacia atrás o adelante en el historial de comandos.
  • CTRL+A / CTRL+E: saltar al inicio o al final de la línea actual.
  • CTRL+B / CTRL+F: moverte un carácter a la izquierda o derecha.
  • ALT+F / ALT+B: moverte palabra a palabra hacia adelante o hacia atrás.
  • CTRL+K: cortar desde la posición actual del cursor hasta el final de la línea, guardándolo en un buffer.
  • CTRL+Y: pegar lo último cortado en la posición del cursor.
  • CTRL+_ o CTRL+X CTRL+U: deshacer la última edición de la línea (puedes repetirlo varias veces).
  • CTRL+L: refrescar la pantalla si la salida se ha quedado hecha un lío.
  • CTRL+C: interrumpir la ejecución del programa que estás depurando.
  • CTRL+X A: activar o desactivar una vista de código fuente dividida en la terminal.
  • CTRL+X 2: abrir una segunda “ventana” (por ejemplo, con ensamblador) y alternar entre vistas.

Una costumbre que se adquiere rápido es tirar mucho del historial con CTRL+P y ENTER cuando repites comandos como run, next o print, y usar CTRL+R (búsqueda inversa, heredada de Readline) para encontrar una orden que ejecutaste hace un rato.

Otros comandos útiles de GDB en el día a día

Aparte de lo ya mencionado, hay un puñado de órdenes que, aunque no uses cada minuto, conviene tener presentes porque resuelven situaciones muy concretas de forma elegante.

  • help: muestra ayuda integrada; help solo enseña temas generales, y help comando entra al detalle de cada orden.
  • info con subcomandos: por ejemplo, info functions para listar funciones del programa, info locals para ver variables locales del frame actual, o info breakpoints para revisar todos los puntos de ruptura.
  • run con argumentos: igual que en la consola normal, puedes pasar parámetros y redirecciones: run 2048 24 4 o run >salida.txt.
  • clear: elimina el breakpoint situado en una función o línea concreta sin necesidad de recordar su número interno.
  • continue: reanuda la ejecución hasta el siguiente breakpoint o el final del programa.
  • quit: termina la sesión de GDB; si el programa sigue vivo, te pedirá confirmación.

Con esta colección de comandos y atajos puedes afrontar prácticamente cualquier sesión de depuración habitual en C o C++: compilar con símbolos, arrancar GDB, parar donde interesa, examinar el estado del programa, alterar variables si hace falta y seguir adelante hasta encontrar el origen del fallo.

Una vez te acostumbras a usar GDB para seguir la ejecución paso a paso, revisar la pila tras un SIGSEGV, vigilar variables con watchpoints y saltar bucles enteros con until, la depuración deja de ser un proceso a base de adivinar y probar y se convierte en una investigación mucho más precisa y controlada, que además mejora tu comprensión del propio código.

tutorial comando gcc
Artículo relacionado:
Tutorial completo del comando gcc y sus opciones clave