Estructuras de Datos y Proyecto Final

3.08 · Debugging Profesional y Git Avanzado

PF-308 ⏱ 90 min ⭐ 120 XP intermedio

🧠 Teoría y conceptos — 🐞 1. ¿Qué es un bug?

Debugging profesional con GDB
Debugging profesional con GDB

Un bug es un comportamiento no deseado del programa. Hay 3 tipos:

Los sintácticos los detecta el compilador (errores de sintaxis). Los runtime crashean con un mensaje. Los lógicos son los difíciles: el programa corre, pero no hace lo que quieres. Esos los atrapa el debugger.

🛠️ 2. Herramientas de debugging: del primitivo al profesional

Nivel 1: cout por todas partes

int suma(int arr[], int n) {

cout << "DEBUG: n = " << n << endl;  // primitivo

int total = 0;

for (int i = 0; i < n; i++) {
cout << "DEBUG: i = " << i << ", arr[i] = " << arr[i] << endl;

total += arr[i];

}

return total;

}

Ventaja: simple, no requiere herramientas. Desventaja: tienes que modificar el código, recompilar, y limpiar después. No escala.

Nivel 2: #define DEBUG

#define DEBUG

#ifdef DEBUG
#define LOG(x) cout << "DEBUG: " << x << endl

#else

#define LOG(x)

#endif

// Uso:

LOG("n = " << n); // solo imprime si DEBUG está definido

Ventaja: puedes activar/desactivar con un comentario. Desventaja: sigue siendo cout, sigue siendo lento.

Nivel 3: debugger de verdad (lo que vamos a usar)

Un debugger te permite:

  • Pausar el programa en una línea específica.
  • Ver el valor de todas las variables en ese momento.
  • Avanzar paso a paso (línea por línea).
  • Entrar en funciones (step-into).
  • Ver la pila de llamadas (call stack).

🎯 3. Tu primer debugging con gdb (línea de comandos)

Compilar con símbolos de debug:

g++ -g -Wall programa.cpp -o programa

# -g incluye información de debug

Ejecutar con gdb:

gdb ./programa

Comandos básicos:

Ejemplo de sesión:

(gdb) break main

Breakpoint 1 at 0x1234

(gdb) run

Starting program: ./programa

Breakpoint 1, main () at programa.cpp:10

(gdb) next # ejecuta línea 10

(gdb) next # ejecuta línea 11

(gdb) print n

$1 = 5

(gdb) next

(gdb) print total

$2 = 0

🖥️ 4. Debugging visual con IDE (más amigable)

En Code::Blocks

  1. Pon un breakpoint haciendo clic en el margen izquierdo junto al número de línea (aparece un punto rojo).
  2. Presiona F8 (o Debug > Start/Continue).
  3. La ejecución se pausa en el breakpoint.
  4. Pasa el mouse sobre cualquier variable para ver su valor.
  5. Usa los botones de la barra de debug:
  • Next line (F7) — step-over
  • Step into (Shift-F7) — entrar a función
  • Continue (F8) — hasta el siguiente breakpoint
  • Stop — terminar debug

En Visual Studio Code

  1. Instala la extensión C/C++ de Microsoft.
  2. Abre la carpeta de tu proyecto.
  3. Configura launch.json (se autogenera con F5).
  4. Pon breakpoints haciendo clic en el margen.
  5. Presiona F5 para iniciar debug.
  6. Panel izquierdo muestra variables, panel derecho muestra call stack.

En Dev-C++

  1. Click en el margen para poner breakpoint.
  2. Menú Debug > Debug (o F5).
  3. Usa los botones de la barra de debug.
**Regla profesional:** un debugger visual es **10x más rápido** que `cout` para bugs complejos. Aprende a usarlo en tu IDE.

🔍 5. Ejemplo paso a paso: encontrar un bug

int sumarPares(int arr[], int n) {

int suma = 0;

for (int i = 0; i < n; i++) {
if (arr[i] % 2 == 0) {

suma += arr[i];

}

}

return suma;

}

int main() {
int datos[] = {1, 2, 3, 4, 5};

int resultado = sumarPares(datos, 5);

cout << "Suma de pares: " << resultado << endl;  // Esperado: 6, obtenido: 4
return 0;

}

El bug: esperabas 6 (2+4) pero da 4.

Debugging paso a paso:

  1. Pon un breakpoint dentro del if.
  2. Inicia debug.
  3. Cuando se pause, revisa arr[i] y arr[i] % 2.
  4. Aha: arr[0] = 1, 1 % 2 == 1 (no es par, no entra). Bien.
  5. Continúa. arr[1] = 2, 2 % 2 == 0 (par, entra), suma = 2. Bien.
  6. Continúa. arr[2] = 3, no entra.
  7. Continúa. arr[3] = 4, entra, suma = 2 + 4 = 6. Bien.
  8. Continúa. arr[4] = 5, no entra.
  9. Loop termina, retorna 6. ¡Pero el programa imprime 4!

El bug no está en la función, está en el main. Sigues trazando y descubres que datos[] se modificó en otra parte. El debugger te lleva al lugar correcto.

🐱 6. Git: de básico a avanzado

Repaso rápido (ya lo viste en Cap 1)

git init # crear repo

git add archivo.cpp # añadir al staging

git commit -m "mensaje" # confirmar cambio

git status # ver estado

git log # ver historial

Lo nuevo: branches (ramas)

Figura: Branches de Git

Concepto: una rama es una línea de desarrollo paralela. La rama principal se llama main (antes master).

git branch # listar ramas

git branch feature-x # crear rama

git checkout feature-x # cambiar a esa rama

git checkout -b feature-x # crear y cambiar (atajo)

¿Por qué usar ramas?

  • Feature branches: cada funcionalidad nueva en su propia rama.
  • Bugfix branches: para arreglar bugs sin tocar lo que funciona.
  • Experimental branches: para probar ideas locas.

Flujo de trabajo típico (Git Flow simplificado)

main (producción)

│

├── develop (integración)

│ │

│ ├── feature/login

│ ├── feature/perfil

│ └── feature/busqueda

│

└── hotfix/bug-critico

🔀 7. Merge: unir ramas

Escenario: tienes una rama feature-x lista. La quieres unir a main.

git checkout main

git merge feature-x

Tipos de merge:

Ejemplo: fast-forward

Antes: A - B (main)

\

C - D (feature-x)

Después de merge:

A - B - C - D (main, feature-x)

Ejemplo: 3-way merge

Antes: A - B - C (main)

\

D - E (feature-x)

Después de merge:

A - B - C - M - F (main, con M = commit de merge, F = feature fusionada)

\ /

D - E ---

💥 8. Resolver conflictos

Cuándo ocurre: dos ramas modificaron la misma línea del mismo archivo.

Ejemplo:

git checkout main

echo "Versión del main" > saludo.txt

git add saludo.txt

git commit -m "main: saludo"

git checkout feature-x

echo "Versión de la feature" > saludo.txt

git add saludo.txt

git commit -m "feature: saludo"

git checkout main

git merge feature-x

# CONFLICTO en saludo.txt

Git marca el conflicto así:

<<<<<<< HEAD

Versión del main

=======

Versión de la feature

>>>>>>> feature-x

Cómo resolver:

  1. Abrir el archivo en conflicto.
  2. Elegir qué versión quieres (o combinarlas).
  3. Borrar las marcas <<<<, ====, >>>>.
  4. Commit el resultado.

git add saludo.txt

git commit -m "merge: resolver conflicto de saludo"

🔄 9. Rebase: historia lineal (alternativa a merge)

git checkout feature-x

git rebase main

Efecto: reaplica tus commits sobre el último commit de main. La historia queda lineal (sin commit de merge).

Antes de rebase:

A - B - C (main)

\

D - E (feature-x)

Después de rebase:

A - B - C (main)

\

D' - E' (feature-x, reaplicados)

Regla de oro: nunca hagas rebase de commits ya pusheados al remoto. Eso reescribe la historia y rompe a tus compañeros.

¿Cuándo usar merge vs rebase?

  • Merge: para integrar features a main (preserva historia real).
  • Rebase: para limpiar tu rama local antes de pedir PR (historia limpia).

📦 10. Comandos avanzados útiles

git stash # guarda cambios sin commitear, vuelve al último commit

git stash pop # recupera los cambios guardados

git log --oneline --graph # ver historia con ramas visualizadas

git diff # ver cambios no commiteados

git diff main..feature-x # comparar dos ramas

git reset --hard HEAD~1 # deshacer último commit (CUIDADO)

git revert <hash> # crear commit que deshace otro commit (SEGURO)

🧪 13. LAB 3.8 — Cazador de Bugs + Feature en Rama (4 h)

Parte A: Debugging (2 h)

Tienes este código con 3 bugs intencionales. Encuéntralos usando el debugger:

#include <iostream>
using namespace std;

int factorial(int n) {

if (n == 0) return 0;  // ¿?

int resultado = 1;

for (int i = 1; i <= n; i++) {
resultado *= i;

}

return resultado;

}

int buscar(int arr[], int n, int objetivo) {

for (int i = 0; i <= n; i++) {  // ¿?
if (arr[i] == objetivo) return i;

}

return -1;

}

int main() {
int nums[] = {5, 3, 8, 1, 9};
cout << "Factorial de 5: " << factorial(5) << endl;  // Esperado 120
cout << "Posición del 8: " << buscar(nums, 5, 8) << endl;  // Esperado 2
cout << "Posición del 100: " << buscar(nums, 5, 100) << endl;  // Esperado -1
return 0;

}

Tarea:

  1. Compilar con -g.
  2. Ejecutar paso a paso con el debugger.
  3. Anotar cada bug encontrado (línea, qué pasa, por qué está mal).
  4. Arreglar el código.
  5. Volver a debuggear para confirmar que ya funciona.

Parte B: Git (2 h)

  1. Crear un repo con el código del LAB 3.6 (ordenamiento).
  2. Hacer 3 commits en main: "agregar burbuja", "agregar selección", "agregar inserción".
  3. Crear una rama feature/quicksort e implementar QuickSort ahí.
  4. Commit en la rama.
  5. Volver a main, hacer un cambio en el main.cpp (por ejemplo, un comentario), commit.
  6. Hacer merge de feature/quicksort a main. ¿Es fast-forward o 3-way?
  7. Borrar la rama de feature.

Rúbrica de evaluación

Bonus XP

  • +30 XP si haces un rebase en vez de merge y comparas los dos historiales.
  • +20 XP si provocas un conflicto intencionalmente y lo resuelves (luego haces commit).
  • +20 XP si usas `git stash` para guardar cambios a medio hacer.
  • +10 XP si subes el repo a GitHub y compartes la URL.

🔚 15. Cierre — Cuaderno del programador

  1. Lista las 5 teclas que más usas en tu debugger (F5, F8, etc.).
  2. Dibuja el árbol de Git de tu LAB 3.6 con todas las ramas y merges.
  3. Reflexión: ¿cuál de los dos (debugger o Git) te parece más útil? ¿Por qué?
  4. Compromiso: la próxima vez que tengas un bug, usa el debugger antes de pedir ayuda a la IA o a un compañero. Te sorprenderás de cuánto encuentras solo.

🏅 Insignia y XP del tema

🔗 ¿Qué sigue?

Boss 3 — Proyecto Integrador: Sistema de Gestión Académico. Es hora de juntar todo: structs, archivos, ordenamiento, búsqueda, matrices. Vas a construir un programa real que gestione alumnos, calificaciones y los guarde en disco. Es la prueba de que dominas los fundamentos.

Antes de avanzar:

  • [ ] LAB 3.8 entregado y funcionando
  • [ ] Quest mixto respondido en el cuaderno
  • [ ] Reflexión escrita

*"Si puedes debuggear tu propio código, puedes aprender cualquier cosa."*

⚠️ Errores típicos — ⚠️ 12. Errores típicos del razonamiento

Error 1: Usar cout en vez del debugger

Síntoma: tu código tiene 50 cout << "DEBUG" por todos lados.

Solución: aprende a usar breakpoints. Son no-invasivos (no tocas el código) y más rápidos.

Error 2: No compilar con -g

g++ programa.cpp -o programa # sin -g, debugger no funciona

g++ -g programa.cpp -o programa # con símbolos de debug

Error 3: Hacer git push --force a main

Esto reescribe la historia remota y puede romper el trabajo de todos. Si necesitas "deshacer" algo en el remoto, usa git revert (crea un commit nuevo que deshace).

Error 4: Trabajar directamente en main

# MAL: todo va directo a main, sin revisión, sin protección

git checkout main

# ... cambios ...

git commit -m "todo"

git push

Bien: crear una rama, hacer cambios, PR, revisión, merge.

Error 5: Miedo a los conflictos

Los conflictos no son errores, son Git diciéndote "ambas partes tocaron esto, decide tú". Resuélvelos con calma, leyendo ambas versiones, no haciendo git checkout --theirs a ciegas.

🤖 IA como copiloto — 🤖 11. AI Mission — "Explica el error, no lo arregles"

Objetivo: que la IA te ayude a entender el bug, no a parchearlo.

Escenario: tu programa crashea con "Segmentation fault" y no sabes por qué.

Prompt sugerido:

"Tengo este código en C++ que crashea con 'Segmentation fault'. El programa debería sumar los elementos de un arreglo pero crashea en la línea marcada. No me des la solución. Explícame qué es un segmentation fault, por qué ocurre, y dame 3 preguntas que me ayuden a encontrar la causa yo mismo."

Lo que NO debes hacer:

  • Pedirle "arregla este código".
  • Copiar su solución sin entender.

Lo que SÍ debes hacer:

  • Usar el debugger (gdb o tu IDE) para ver dónde crashea.
  • Hacerte las 3 preguntas que la IA te dio.
  • Diagnosticar tú mismo antes de tocar el código.

Ritual de 3 min: si la IA no te da buenas preguntas, pregúntale: "¿qué tendría que ver en el debugger para encontrar esto?" — eso te lleva a la acción correcta.

📝 Quest · Cuestionario — 📝 Quest — Reactivos para autoevaluación

Las siguientes preguntas te sirven para autoevaluarte después de leer el tema. Responde en tu cuaderno o mentalmente, y luego revisa las Respuestas modelo (disponibles en libre acceso, sin iniciar sesión).

Sección A · Opción múltiple

Elige la opción correcta (A, B, C o D). Las respuestas están en la sección Respuestas modelo.

  1. ¿Qué es un breakpoint?
    • Un error de compilación
    • Un punto donde el debugger pausa la ejecución
    • Un tipo de variable
    • Una clase
  2. ¿Qué es step-over en un debugger?
    • Terminar
    • Ejecutar la línea actual sin entrar en funciones
    • Reiniciar
    • Compilar
  3. ¿Qué es step-into?
    • Saltar línea
    • Entrar en la función llamada
    • Terminar
    • Compilar
  4. ¿Qué hace watch en un debugger?
    • Muestra el tiempo
    • Monitorea el valor de una expresión/variable
    • Compila
    • Reinicia
  5. ¿Qué es git stash?
    • Borra cambios
    • Guarda cambios sin commitear para cambiar de rama
    • Hace commit
    • Sube al remoto
  6. ¿Qué es git rebase -i?
    • Borra commits
    • Rebase interactivo: reordenar, fusionar, editar commits
    • Sube
    • Hace merge
  7. ¿Diferencia entre merge y rebase?
    • Son iguales
    • merge preserva, rebase reescribe
    • merge es para borrar
    • rebase es más lento
  8. ¿Qué es un commit hook en Git?
    • Un bug
    • Script que se ejecuta en eventos de Git (pre-commit, post-commit)
    • Un merge
    • Un branch
  9. ¿Para qué sirve git tag?
    • Borrar commits
    • Marcar commits importantes (releases)
    • Hacer merge
    • Crear ramas
  10. ¿Qué es git bisect?
    • Crear commit
    • Búsqueda binaria en historial para encontrar el commit que introdujo un bug
    • Borrar
    • Compilar

Sección B · Preguntas abiertas

Desarrolla tu respuesta en al menos 3 líneas. Compara con la respuesta modelo después de escribir.

  1. Explica tu flujo de trabajo cuando encuentras un bug. ¿Qué herramientas usas?
    → Escribe tu respuesta aquí (en tu cuaderno o mentalmente)…
  2. ¿Qué es git bisect y cuándo lo usarías?
    → Escribe tu respuesta aquí (en tu cuaderno o mentalmente)…
  3. ¿Por qué es importante un buen mensaje de commit en lugar de "fix" o "wip"?
    → Escribe tu respuesta aquí (en tu cuaderno o mentalmente)…

🟢 Cuando termines, revisa las Respuestas modelo y compáralas con las tuyas. La mejor forma de aprender es discutir cada respuesta contigo mismo o con un compañero.

← Volver a temas del capítulo 📚 Ver todos los temas 🔑 Inicia sesión para hacer el Test