Why -O0? With optimization, variables may vanish into registers and lines disappear. Step/print won't behave as you expect. Always -O0 while debugging.
Sample buggy program
// sum.c: should compute 1+2+...+N (has a bug)#include<stdio.h>intsum_to(int n) {
int s = 0;
for (int i = 1; i < n; i++) { // BUG: should be i <= n
s += i;
}
return s;
}
intmain(void) {
int r = sum_to(10);
printf("sum = %d\n", r); // expected 55, got 45return0;
}
Basic session: break / run / step / print
$ gcc -g sum.c -o sum
$ gdb ./sum
(gdb)break sum_to# breakpoint at function start
Breakpoint 1 at 0x1149: file sum.c, line 5.
(gdb)run# start program
Breakpoint 1, sum_to (n=10) at sum.c:5
5 int s = 0;
(gdb)next# advance one line (don't step into calls)
6 for (int i = 1; i < n; i++) {
(gdb)print n
$1 = 10
(gdb)print i
No symbol "i" in current context. # not declared yet(gdb)next
7 s += i;
(gdb)print i
$2 = 1 # now i=1(gdb)continue
sum = 45 # wrong! expected 55
Conditional breakpoints
(gdb)break sum.c:7 if i == 5(gdb)run
Breakpoint 2, sum_to at sum.c:7
(gdb)print s
$3 = 10 # 0+1+2+3+4 = 10 β
step vs next:step (s) descends into called functions; next (n) treats a call as one line.
Auto-print with display
(gdb)display i# print at every stop(gdb)display s(gdb)next
1: i = 3
2: s = 3
Stack trace (bt / frame)
When a bug happens deep inside a call chain, you want to know who called the current function.
(gdb)backtrace# or 'bt'
#0 sum_to (n=10) at sum.c:7
#1 main () at sum.c:13
(gdb)frame 1# jump to main's frame
#1 main () at sum.c:13
13 int r = sum_to(10);
(gdb)print r
$4 = 0 # still 0, the call isn't finished(gdb)frame 0# back to the inner frame
Debugging recursion:bt shows every depth; switch between them with frame N and read the args.
Watchpoints (stop when a variable changes)
Perfect for "somehow this variable has the wrong value" bugs.
(gdb)break main(gdb)run(gdb)watch r# watch writes to r
Hardware watchpoint 2: r
(gdb)continue
Hardware watchpoint 2: r
Old value = 0
New value = 45
main () at sum.c:13
Scope: watching a local variable is released when its function returns. For globals or heap memory use watch *ptr / watch -location.
Debugging a segfault
When run from gdb, a crash stops right at the offending instruction.