← HUB ✦ Holberton School — Debugging ✦

GDB

GNU Debugger — Traque les bugs. Lis la mémoire.

C'EST QUOI GDB ?
🔬 DÉFINITION — POUR LES DÉBUTANTS

GDB (GNU Debugger) est un outil qui te permet d'exécuter ton programme en mode ralenti, de l'arrêter à n'importe quelle ligne, d'inspecter les variables, la mémoire et la pile d'appels. C'est ton meilleur allié contre les segfaults et les bugs impossibles à trouver avec des printf.

🕹️ ANALOGIE GAMING

GDB c'est le mode debug d'un jeu — tu peux mettre le jeu en pause (breakpoint) à n'importe quel moment, avancer image par image (step), regarder la valeur de chaque variable comme si tu avais accès aux stats cachées, et même voyager dans le passé pour voir comment un bug s'est produit (backtrace).

⏸️
BREAKPOINT

Pause le programme à une ligne précise

👁️
INSPECT

Lire variables, mémoire, registres

🪜
STEP

Avancer ligne par ligne dans le code

ÉTAPE -1 — INSTALLER GDB
📦 INSTALLER GDB SUR UBUNTU/DEBIAN
# Vérifier si GDB est déjà installé
which gdb
# /usr/bin/gdb   → déjà installé ✅
# (rien)         → pas installé, à installer

# Installer GDB
sudo apt-get update
sudo apt-get install gdb

# Vérifier la version
gdb --version
GNU gdb (Ubuntu 12.1) 12.1 ...
Sur les sandboxes Holberton, GDB est généralement pré-installé. Si tu obtiens command not found, lance le apt-get install ci-dessus.
ÉTAPE 0 — COMPILER AVEC -g
⚙️ COMPILER POUR GDB — LE FLAG -g

Sans -g, GDB ne peut pas afficher les noms de variables, les numéros de lignes ou le code source. C'est le premier réflexe à avoir.

# ❌ Sans -g : GDB travaille à l'aveugle (adresses hexadécimales seulement)
gcc -Wall -Werror -Wextra main.c -o prog

# ✅ Avec -g : GDB voit le code source, les variables, les lignes
gcc -Wall -Werror -Wextra -g main.c -o prog

# Convention Holberton complète avec -g
gcc -Wall -Werror -Wextra -pedantic -std=gnu89 -g main.c -o prog

# Vérifier que les symboles de debug sont bien inclus
file prog
# prog: ELF 64-bit ... with debug_info, not stripped ← ✅
Le flag -g rend le binaire plus lourd et plus lent — ne jamais l'utiliser en production. C'est uniquement pour le debug en local.
LANCER GDB
🚀 DÉMARRER UNE SESSION GDB
# Lancer GDB avec ton programme
gdb ./prog

# Lancer avec des arguments (argv)
gdb --args ./prog arg1 arg2

# Lancer et exécuter directement (sans prompt interactif)
gdb -batch -ex run ./prog

# Analyser un core dump (fichier de crash)
gdb ./prog core

# Mode quiet (sans le message de bienvenue)
gdb -q ./prog
CE QUE TU VOIS AU DÉMARRAGE :
GNU gdb (Ubuntu 12.1) 12.1
Reading symbols from ./prog...
(gdb) ← le prompt GDB, tu peux taper tes commandes ici
Dans GDB, tu peux abréger toutes les commandes à leur forme courte. break → b, run → r, next → n, etc. Appuyer sur Entrée vide répète la dernière commande.
LES COMMANDES FONDAMENTALES
▶️ EXÉCUTER ET CONTRÔLER LE PROGRAMME
# ── LANCER / ARRÊTER ───────────────────────────────────

(gdb) run                       # r — lancer le programme
(gdb) run arg1 arg2             # lancer avec des arguments
(gdb) quit                      # q — quitter GDB
(gdb) kill                      # arrêter le programme en cours

# ── AVANCER DANS LE CODE ────────────────────────────────

(gdb) next                      # n — ligne suivante (sans entrer dans les fonctions)
(gdb) step                      # s — ligne suivante (ENTRE dans les fonctions)
(gdb) continue                  # c — reprendre jusqu'au prochain breakpoint
(gdb) finish                    # fin — terminer la fonction courante et s'arrêter
(gdb) until 25                 # avancer jusqu'à la ligne 25
(gdb) next 5                   # avancer de 5 lignes
next vs step : next (n) exécute une fonction en entier sans y entrer. step (s) entre à l'intérieur de la fonction pour debugger ligne par ligne dedans. En débutant, commence par next.
BREAKPOINTS — PAUSER LE PROGRAMME
⏸️ POSER ET GÉRER DES BREAKPOINTS
# ── CRÉER UN BREAKPOINT ────────────────────────────────

(gdb) break main               # b main — pause au début de main()
(gdb) break 42                 # b 42 — pause à la ligne 42
(gdb) break main.c:42          # pause ligne 42 du fichier main.c
(gdb) break my_function        # pause au début de my_function()

# ── BREAKPOINT CONDITIONNEL ─────────────────────────────

(gdb) break 42 if i == 5      # pause ligne 42 seulement si i vaut 5
(gdb) break loop if ptr == NULL # pause si ptr est NULL

# ── GÉRER LES BREAKPOINTS ──────────────────────────────

(gdb) info breakpoints          # i b — voir tous les breakpoints
(gdb) delete 1                  # d 1 — supprimer le breakpoint #1
(gdb) delete                    # supprimer TOUS les breakpoints
(gdb) disable 1                # désactiver (sans supprimer)
(gdb) enable 1                 # réactiver

# Exemple — session typique de debugging
(gdb) break main
Breakpoint 1 at 0x401130: file main.c, line 8.
(gdb) run
Breakpoint 1, main () at main.c:8
8           int i = 0;
(gdb) ← programme pausé à la ligne 8 !
INSPECTER LES VARIABLES ET LA MÉMOIRE
👁️ PRINT — LIRE LES VALEURS
# ── PRINT (p) — afficher une valeur ────────────────────

(gdb) print i                  # p i — valeur de la variable i
$1 = 42

(gdb) print ptr               # afficher un pointeur
$2 = (int *) 0x602260

(gdb) print *ptr              # déréférencer — valeur pointée
$3 = 7

(gdb) print str               # afficher une string
$4 = 0x402010 "Hello"

(gdb) print arr[3]            # valeur à l'index 3 d'un tableau
(gdb) print node->n           # champ d'une struct via pointeur
(gdb) print 2 + 2             # GDB peut évaluer des expressions

# ── FORMATS D'AFFICHAGE ─────────────────────────────────

(gdb) print/x i               # en hexadécimal
$5 = 0x2a
(gdb) print/d i               # en décimal (défaut)
(gdb) print/b i               # en binaire
(gdb) print/c 65              # comme caractère → 'A'
(gdb) print/s ptr             # comme string (char*)
(gdb) print/f x               # comme float
🧠 DISPLAY ET WATCH — SURVEILLANCE AUTOMATIQUE
# display — affiche automatiquement à chaque step
(gdb) display i               # affiche i après chaque next/step
(gdb) display *ptr            # surveille la valeur pointée
(gdb) undisplay 1             # arrêter la surveillance #1
(gdb) info display            # voir les surveillances actives

# watch — pause quand la valeur CHANGE (watchpoint)
(gdb) watch i                 # pause quand i est modifié
(gdb) watch *ptr              # pause quand *ptr change
(gdb) rwatch i                # pause quand i est LU

# Exemple : display i pendant une boucle
(gdb) display i
(gdb) next
1: i = 0
9           while (i < 5)
(gdb) next
1: i = 1    ← i s'affiche automatiquement !
display est ton meilleur ami pour les boucles — tu n'as pas à retaper print i à chaque step. Il affiche automatiquement.
🔭 PRINT AVANCÉ — STRUCTS, TYPES ET PLUS
# ── AFFICHER UNE STRUCT ENTIÈRE ────────────────────────

# Afficher tous les champs d'une struct via pointeur
(gdb) print *node
$1 = {n = 42, next = 0x602280}

# Afficher une variable struct (pas un pointeur)
(gdb) print user
$2 = {name = 0x402010 "Alice", age = 25, next = 0x0}

# ── PTYPE — VOIR LE TYPE D'UNE VARIABLE ────────────────

(gdb) ptype node
type = struct node_s {
    int n;
    struct node_s *next;
} *

(gdb) ptype i
type = int

(gdb) ptype argv
type = char **

# ── WHATIS — TYPE COURT (sans les détails) ──────────────

(gdb) whatis node
type = node_t *

# ── AFFICHER UN TABLEAU COMPLET ─────────────────────────

(gdb) print *arr@5           # afficher 5 éléments depuis arr
$3 = {1, 2, 3, 4, 5}
print *node (avec l'étoile) est indispensable pour les structs — il déréférence le pointeur et affiche tous les champs d'un coup. Sans l'étoile, tu ne vois que l'adresse.
💾 EXAMINE (x) — LIRE LA MÉMOIRE BRUTE

La commande x (examine) te permet de lire directement des octets en mémoire. Syntaxe : x/NFS ADRESSE où N=nombre, F=format, S=taille.

# x/NFS addr — N=nombre, F=format, S=taille d'unité

# ── FORMATS (F) ────────────────────────────────────────
# x=hex  d=decimal  s=string  i=instruction  c=char  b=binary

# ── TAILLES (S) ────────────────────────────────────────
# b=byte(1)  h=halfword(2)  w=word(4)  g=giant(8)

# Exemples concrets :
(gdb) x/4xb ptr               # 4 octets en hex depuis l'adresse ptr
0x602260: 0x48 0x65 0x6c 0x6c   ← "Hell"

(gdb) x/s str                 # lire une string depuis cette adresse
0x402010: "Hello World"

(gdb) x/4dw arr               # 4 ints depuis le début du tableau arr
0x7fff...: 1    2    3    4

(gdb) x/10i main              # 10 instructions assembleur de main
0x401130 <main>: push %rbp
0x401131 <main+1>: mov %rsp,%rbp

(gdb) x/1xg &i                # 1 giant (8 bytes) à l'adresse de i
INSPECTER LE PROGRAMME ET LA PILE
🗺️ INFO — TOUT SAVOIR SUR L'ÉTAT DU PROGRAMME
# ── VARIABLES ET PORTÉE ────────────────────────────────

(gdb) info locals              # TOUTES les variables locales de la fonction
i = 3
ptr = 0x602260
str = 0x402010 "hello"

(gdb) info args                # les arguments de la fonction courante
(gdb) info variables           # toutes les variables globales

# ── PILE D'APPELS ──────────────────────────────────────

(gdb) backtrace                # bt — pile d'appels complète
#0  ft_strlen (str=0x0) at ft_strlen.c:7    ← fonction actuelle
#1  0x00401155 in main () at main.c:12      ← qui a appelé

(gdb) backtrace full           # bt full — avec les variables locales
(gdb) frame 1                  # aller dans le frame #1 (main)
(gdb) up                       # remonter d'un frame
(gdb) down                     # descendre d'un frame

# ── REGISTRES ─────────────────────────────────────────

(gdb) info registers           # i r — tous les registres CPU
(gdb) info registers rax      # un registre précis
(gdb) print $rsp               # valeur du stack pointer
(gdb) print $rip               # instruction pointer (ligne courante)

# ── CODE SOURCE ───────────────────────────────────────

(gdb) list                     # l — afficher les 10 lignes autour de la position
(gdb) list main               # code autour de main()
(gdb) list 1,50               # lignes 1 à 50
(gdb) where                    # où suis-je ? (alias de backtrace)
Le backtrace (bt) est la commande la plus importante pour comprendre un crash. Il te montre exactement la chaîne d'appels qui a mené au bug : de main() jusqu'à la fonction fautive.
LES REGISTRES CPU — COMPRENDRE L'ESSENTIEL
⚡ LES REGISTRES IMPORTANTS EN X86-64
rip Instruction Pointer
Ligne en cours d'exécution
rsp Stack Pointer
Sommet de la pile
rbp Base Pointer
Base du frame courant
rax Return Value
Valeur de retour d'une fonction
rdi 1er argument
passé à une fonction
rsi 2ème argument
passé à une fonction
rdx 3ème argument
passé à une fonction
rbx Registre général
sauvegardé
# Voir un registre précis
(gdb) print $rip       # adresse de l'instruction courante
(gdb) print $rax       # valeur de retour (après un call)
(gdb) info registers   # tous les registres d'un coup
MODIFIER DES VALEURS EN COURS D'EXÉCUTION
✏️ SET — CHANGER UNE VALEUR À LA VOLÉE

GDB te permet de modifier des variables pendant l'exécution pour tester différents scénarios sans recompiler.

# Changer la valeur d'une variable
(gdb) set variable i = 10     # forcer i à 10
(gdb) set var ptr = 0         # mettre ptr à NULL

# Appeler une fonction manuellement
(gdb) call printf("hello\n")  # appeler printf depuis GDB
(gdb) call ft_strlen("test")  # tester une de tes fonctions

# Changer un registre
(gdb) set $rax = 0            # forcer la valeur de retour

# Retourner d'une fonction avec une valeur précise
(gdb) return 42               # quitter la fonction et retourner 42
MODE TUI — GDB AVEC INTERFACE VISUELLE
🖥️ TUI — TEXT USER INTERFACE

Le mode TUI affiche le code source en temps réel dans une fenêtre séparée, avec une flèche qui indique la ligne courante. Bien plus confortable que de taper list à chaque fois.

# Activer le mode TUI au lancement
gdb -tui ./prog

# Activer/désactiver pendant une session
(gdb) tui enable             # activer la fenêtre de code
(gdb) tui disable            # revenir au mode normal

# Raccourci clavier
Ctrl + X, puis A           # toggle TUI on/off
Ctrl + X, puis 2           # split : code + assembleur

# Naviguer dans la fenêtre TUI
# Les flèches ↑↓ scrollent dans le code source
# Ctrl+L rafraîchit l'affichage si l'écran est corrompu
CE QUE TU VOIS EN MODE TUI :
┌── main.c ──────────────────────────────────────────────────┐
│ 8 int i = 0; │
│ 9 int arr[5] = {1, 2, 3, 4, 5}; │
│ ►10 while (i < 5) │
│ 11 { │
│ 12 arr[i] = arr[i] * 2; │
└────────────────────────────────────────────────────────────┘
La flèche ► indique la ligne en cours d'exécution — elle bouge à chaque next/step
Le mode TUI est fortement recommandé pour les débutants — tu vois exactement où tu en es dans le code sans avoir à taper list en permanence. Lance toujours GDB avec gdb -tui ./prog.
ERREURS GDB FRÉQUENTES — ET LEURS SOLUTIONS
🚨 MESSAGES D'ERREUR COURANTS
No debugging symbols found
Reading symbols from ./prog...
(No debugging symbols found in ./prog)
# → Tu n'as pas compilé avec -g !
# Solution : recompiler avec -g
gcc -g main.c -o prog
No symbol table is loaded
(gdb) break main
No symbol table is loaded. Use the "file" command.
# → GDB a été lancé sans programme
# Solution : charger le programme
(gdb) file ./prog
# Ou relancer : gdb ./prog
?? () dans le backtrace
(gdb) backtrace
#0  0x00007f8... in ?? ()
#1  0x0000000... in ?? ()
# → Fonction sans symboles de debug (bibliothèque externe)
# → OU programme compilé sans -g
# → Recompile avec -g pour voir les noms
Cannot access memory at address 0x0
(gdb) print *ptr
Cannot access memory at address 0x0
# → ptr est NULL ! Tu essaies de déréférencer NULL
(gdb) print ptr        # afficher l'adresse d'abord
$1 = (int *) 0x0   ← confirme que ptr = NULL
Inferior 1 exited normally
Inferior 1 (process 12345) exited normally.
# → Le programme a terminé sans crash — c'est normal !
# Relancer avec : run
# Ou poser un breakpoint AVANT de lancer
ANALYSER UN CRASH — LES CAS COURANTS
💥 SEGFAULT — TROUVER LA CAUSE
💀 SEGMENTATION FAULT

Ton programme accède à une adresse mémoire invalide — pointeur NULL, pointeur non initialisé, tableau hors limites, stack overflow...

# Ton programme crash → lancer GDB
gdb ./prog

(gdb) run
Program received signal SIGSEGV, Segmentation fault.
0x0000000000401145 in ft_strlen (str=0x0) at ft_strlen.c:7
7           while (*str)

# → Le crash est à ft_strlen.c ligne 7, str vaut 0x0 (NULL) !

# Voir la pile d'appels pour comprendre qui a appelé ft_strlen
(gdb) backtrace
#0  ft_strlen (str=0x0) at ft_strlen.c:7      ← la fonction crashée
#1  0x00401165 in main () at main.c:12         ← main a appelé ft_strlen

# Aller dans main pour voir ce qui a été passé
(gdb) frame 1
(gdb) info locals
str = 0x0                                      ← str est NULL ! Voilà le bug.

# Checklist quand tu vois un segfault :
# 1. run → lire le message d'erreur (fichier + ligne)
# 2. backtrace → voir la chaîne d'appels
# 3. frame N → aller dans chaque frame
# 4. info locals / print VAR → trouver la valeur invalide
♾️ BOUCLE INFINIE — LE PROGRAMME NE RÉPOND PLUS
# Ton programme est bloqué (boucle infinie)
# → Lance GDB, run, puis Ctrl+C pour l'interrompre

gdb ./prog
(gdb) run
# [le programme tourne et ne revient pas...]
# Appuie sur Ctrl+C
^C
Program received signal SIGINT, Interrupt.
0x0000000000401155 in my_loop (head=0x602260) at main.c:23
23          while (node != NULL)

(gdb) backtrace
#0  my_loop (head=0x602260) at main.c:23

(gdb) print *node
$1 = {n = 42, next = 0x602260}   ← next pointe sur lui-même ! Cycle !

# → next pointe vers le nœud lui-même → boucle infinie
# Solution : vérifier la logique d'ajout de nœuds
# Quelque part, node->next = node au lieu de node->next = NULL
Ctrl+C dans GDB = interrompre le programme SANS le tuer. Tu peux ensuite inspecter l'état exact au moment où il était bloqué. C'est le moyen le plus rapide de localiser une boucle infinie.
💥 DOUBLE FREE — CRASH ALÉATOIRE
Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50

(gdb) backtrace
#0  __GI_raise ...
#1  __GI_abort ...
#2  __libc_message ...
#3  malloc_printerr ...          ← malloc détecte une corruption
#4  free (mem=0x602260) at ...   ← le double free est ici
#5  0x00401188 in main () at main.c:35

# SIGABRT = le système a détecté une corruption du heap
# Causes : double free, écriture hors des limites d'un malloc...
# → Utilise Valgrind pour identifier précisément le double free

(gdb) frame 5
(gdb) info locals            # voir les variables au moment du crash
Pour les bugs liés au heap (double free, use-after-free), Valgrind est souvent plus efficace que GDB seul : valgrind --leak-check=full ./prog. GDB et Valgrind sont complémentaires.
WORKFLOW COMPLET — SESSION TYPE
🎯 UNE SESSION GDB DE A À Z
1

Compiler avec -g

gcc -Wall -Werror -Wextra -g main.c -o prog
2

Lancer GDB et poser un breakpoint

gdb ./prog
(gdb) break main          # pause au début de main
3

Lancer et inspecter

(gdb) run
(gdb) list                  # voir le code autour
(gdb) info locals           # voir toutes les variables locales
4

Avancer et surveiller

(gdb) display i            # surveiller i
(gdb) next                  # avancer ligne par ligne
(gdb) next                  # Entrée seule = répéter next
5

Entrer dans une fonction suspecte

(gdb) step                  # entrer dans la fonction
(gdb) print *ptr           # inspecter le pointeur
(gdb) finish                # sortir de la fonction
6

Corriger et recommencer

(gdb) kill                  # arrêter l'exécution
(gdb) run                   # relancer depuis le début
(gdb) quit                  # quitter GDB
TIPS ET ASTUCES UTILES
⚡ GAGNER DU TEMPS DANS GDB
Entrée vide = répéter la dernière commande

Appuyer sur Entrée sans rien taper répète la dernière commande. Parfait pour avancer rapidement avec next ou continue.

Utilise les abréviations
b   = break    r   = run      n  = next
s   = step     c   = continue p  = print
bt  = backtrace l  = list     q  = quit
i b = info breakpoints        i l = info locals
d   = delete   fin = finish   k  = kill
Le fichier .gdbinit — automatise tes sessions

Crée un .gdbinit à la racine de ton projet pour ne plus retaper les mêmes commandes à chaque fois :

# .gdbinit — exécuté automatiquement au démarrage de GDB
set confirm off           # supprimer les confirmations
set print pretty on       # structs affichées joliment
set print array on        # tableaux sur plusieurs lignes
tui enable                # activer le mode TUI par défaut
break main                # poser un breakpoint sur main
run                       # lancer automatiquement
GDB peut refuser de charger un .gdbinit non sécurisé. Si tu vois un avertissement, ajoute dans ~/.gdbinit : set auto-load safe-path /
Chercher une adresse → quel symbole ?
(gdb) info symbol 0x401130
main in section .text   ← cette adresse correspond à main()
Passer des arguments avec run
(gdb) run hello 42 "avec espace"
# Équivalent à ./prog hello 42 "avec espace"

# Avec redirection
(gdb) run < input.txt
Aide intégrée dans GDB
(gdb) help                  # liste des catégories
(gdb) help break            # aide sur la commande break
(gdb) help all              # toutes les commandes
TABLEAU RÉCAP — TOUTES LES COMMANDES
📊 RÉFÉRENCE RAPIDE COMPLÈTE
CommandeAbrév.Ce que ça fait
run [args]rLancer le programme (avec args optionnels)
quitqQuitter GDB
killkArrêter le programme en cours
break <loc>bPoser un breakpoint (ligne, fonction, fichier:ligne)
break <loc> if <cond>Breakpoint conditionnel
info breakpointsi bLister tous les breakpoints
delete [N]dSupprimer le breakpoint N (ou tous)
next [N]nLigne suivante (sans entrer dans les fonctions)
step [N]sLigne suivante (entre dans les fonctions)
continuecReprendre jusqu'au prochain breakpoint
finishfinTerminer la fonction courante et s'arrêter
until <N>Avancer jusqu'à la ligne N
print <expr>pAfficher la valeur d'une expression / variable
print/x /d /s /cAfficher en hex / décimal / string / char
display <expr>Afficher automatiquement après chaque step
watch <var>Pause quand la variable change
x/NFS <addr>Examiner N unités de mémoire brute
info localsi lToutes les variables locales du frame courant
info argsArguments de la fonction courante
info registersi rTous les registres CPU
backtracebtPile d'appels complète
frame <N>Aller dans le frame N de la pile
list [loc]lAfficher le code source autour de la position
set variable <v> = <val>Modifier une variable à la volée
call <func(args)>Appeler une fonction manuellement
print *struct_ptrAfficher tous les champs d'une struct
print *arr@NAfficher N éléments d'un tableau
ptype <var>Afficher le type complet d'une variable
whatis <var>Afficher le type court d'une variable
tui enableActiver l'interface visuelle avec code source
help [cmd]Aide sur une commande
RÉSUMÉ — MÉTHODE ANTI-SEGFAULT
🏆 CHECKLIST DÉBUTANT POUR TOUT BUG
╔══════════════════════════════════════════════════════════════════╗
║  ÉTAPE 0 — TOUJOURS compiler avec -g                             ║
╚══════════════════════════════════════════════════════════════════╝
gcc -g -Wall -Werror -Wextra main.c -o prog

╔══════════════════════════════════════════════════════════════════╗
║  FACE À UN CRASH (segfault, etc.)                                ║
╚══════════════════════════════════════════════════════════════════╝
gdb ./prog
(gdb) run                    # laisser crasher
(gdb) backtrace              # voir où et comment
(gdb) info locals            # inspecter les variables locales
(gdb) frame 1               # remonter d'un niveau

╔══════════════════════════════════════════════════════════════════╗
║  POUR TRACER L'EXÉCUTION LIGNE PAR LIGNE                         ║
╚══════════════════════════════════════════════════════════════════╝
(gdb) break main            # pause au début
(gdb) run
(gdb) display ma_var        # surveiller une variable
(gdb) next                   # [Entrée] pour répéter

╔══════════════════════════════════════════════════════════════════╗
║  RÈGLES D'OR GDB                                                 ║
╚══════════════════════════════════════════════════════════════════╝
# ✅ Toujours compiler avec -g avant de lancer GDB
# ✅ Lancer GDB avec -tui pour voir le code en temps réel
# ✅ Commencer par backtrace (bt) sur tout crash
# ✅ Utiliser info locals pour voir toutes les variables
# ✅ Utiliser print *ptr pour inspecter une struct entière
# ✅ Utiliser display pour surveiller une var dans une boucle
# ✅ Ctrl+C pour interrompre une boucle infinie
# ✅ Utiliser break conditionnel si la boucle a 1000 itérations
# ✅ Combiner GDB + Valgrind pour les bugs mémoire (double free...)
# ✅ Entrée vide = répéter → avancer vite
# ❌ Ne pas utiliser GDB sans -g → tu vois que des adresses hex
# ❌ Ne pas confondre next (reste dans la fonction) et step (y entre)