Debugging with GDB
Master the GNU Debugger! Learn to install GDB on Windows, Linux, and macOS, set breakpoints, step through code, inspect variables, and find bugs like a professional developer.
Track Your Progress
Sign in to save your learning progress
What You Will Learn
- ✓Install GDB on Windows, Linux, and macOS
- ✓Compile programs with debug symbols (-g flag)
- ✓Use essential GDB commands (run, break, step, print)
- ✓Set conditional breakpoints and watchpoints
- ✓Inspect variables, arrays, and memory
- ✓Debug segmentation faults and crashes
- ✓Use GDB TUI mode for visual debugging
- ✓Know about other debugging tools (Valgrind, AddressSanitizer)
?Why Learn GDB?
Unlike Python or JavaScript, C programs don't have helpful error messages. You get Segmentation fault and nothing else. GDB is your eyes into the program.
Inspect Variables
See exact values at any point
Pause Execution
Breakpoints stop at exact lines
Find Crashes
Locate exact crash location
Interview tip: Companies expect developers to know how to use a debugger. "Add printf statements" is not a professional debugging strategy!
01What is GDB?
GDB (GNU Debugger) is the most widely-used debugger for C/C++ programs. It lets you see what's happening inside your program while it runs — pause execution, inspect variables, step through code line by line, and find exactly where bugs occur.
Debugging Without GDB
printf("x = %d\\n", x);
printf("got here 1\\n");
printf("got here 2\\n");
printf("why is this %d?\\n", y);
printf("HELP!!!\\n");
Messy, slow, and frustrating
Debugging With GDB
(gdb) break main
Breakpoint 1 at 0x1149
(gdb) run
(gdb) print x
$1 = 42
Professional and efficient!
What Can GDB Do?
✓ Set breakpoints — pause at specific lines
✓ Step through code — execute one line at a time
✓ Inspect variables — see current values
✓ Examine memory — look at raw memory
✓ Analyze crashes — find where/why it crashed
✓ Watch variables — break when values change
02The Debugging Workflow
Typical GDB Session
gcc -g program.c
Compile with debug info
gdb ./a.out
Start GDB
break main
Set breakpoint
run
Start program
next / step
Execute line
print var
Inspect value
Found the bug!
Fix code and repeat
Essential GDB Commands Cheat Sheet
Navigation
run - Start program
next - Next line (skip functions)
step - Next line (enter functions)
continue - Run until next breakpoint
finish - Run until function returns
Inspection
print x - Show variable value
print *ptr - Dereference pointer
print arr[0]@10 - Show array
backtrace - Show call stack
info locals - All local vars
Breakpoints
break main - Break at function
break 42 - Break at line 42
break if x>10 - Conditional break
delete 1 - Delete breakpoint #1
info break - List breakpoints
Control
quit - Exit GDB
kill - Stop program
set var x=5 - Change variable
list - Show source code
help cmd - Get help
03Installing GDB
GDB is available on all major operating systems. Choose your platform below:
Linux Installation
Ubuntu / Debian:
sudo apt update
sudo apt install gdb
Fedora / Red Hat / CentOS:
sudo dnf install gdb
Arch Linux:
sudo pacman -S gdb
Verify installation:
gdb --version
GNU gdb (Ubuntu 12.1-0ubuntu1) 12.1
Windows Installation
Option 1: MinGW-w64 (Recommended)
- Download MinGW-w64 from mingw-w64.org or use MSYS2
- During installation, GDB is included with the toolchain
- Add to PATH:
C:\mingw64\bin
Option 2: MSYS2 (Easiest)
# Install MSYS2 from msys2.org, then in MSYS2 terminal:
pacman -S mingw-w64-x86_64-gdb
Option 3: Using WSL (Windows Subsystem for Linux)
# In WSL terminal (Ubuntu):
sudo apt install gdb
Verify in Command Prompt or PowerShell:
gdb --version
macOS Installation
Note: On macOS, LLDB is recommended over GDB as it's the default debugger and integrates better with the system. However, you can still install GDB if needed.
Using Homebrew:
# Install Homebrew if not installed
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install GDB
brew install gdb
Important: Code Signing Required
On macOS, GDB requires code signing to control other processes. This is a security requirement.
- Open Keychain Access → Certificate Assistant → Create Certificate
- Name:
gdb-cert, Identity Type: Self Signed Root, Certificate Type: Code Signing - Trust: Always Trust for Code Signing
- Sign GDB:
codesign -s gdb-cert $(which gdb)
Easier Alternative: Use LLDB instead! It comes pre-installed with Xcode Command Line Tools: xcode-select --install
03Compiling for Debugging
Before using GDB, you must compile with the -g flag to include debug information. This tells the compiler to embed extra data (variable names, line numbers, etc.) into the executable.
Without -g flag
gcc program.c -o program
No debug info — can't see variable names or source code
✓With -g flag
gcc -g program.c -o program
Full debug info — see everything!
# Recommended: Include warnings and debug symbols
gcc -g -Wall -Wextra program.c -o program
# For optimization debugging (when release build has issues)
gcc -g -O2 program.c -o program
# Start GDB with your program
gdb ./program
Debug Build vs Release Build
Debug builds (-g) are larger and slower. For production, use -O2 without -g. You can combine both for debugging optimized code, but variable values may appear wrong due to optimization.
04Starting GDB
# Basic: Start GDB with your program
gdb ./program
# With arguments: Pass args to your program
gdb --args ./program arg1 arg2
# Quiet mode: Skip the intro message
gdb -q ./program
# TUI mode: Visual source code display
gdb -tui ./program
# Attach to running process
gdb -p <process-id>
# Debug a core dump
gdb ./program core
GDB Interface
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
...
Reading symbols from ./program...
(gdb) _
The (gdb) prompt means GDB is ready for commands.
05Essential GDB Commands
GDB commands can be abbreviated to their shortest unique prefix. For example, break can be written as b.
| Command | Short | Description | Example |
|---|---|---|---|
| Running | |||
| run | r | Start program execution | run arg1 arg2 |
| continue | c | Continue after breakpoint | c |
| kill | k | Stop the program | kill |
| quit | q | Exit GDB | quit |
| Breakpoints | |||
| break | b | Set a breakpoint | b main, b 25, b file.c:10 |
| info breakpoints | i b | List all breakpoints | info break |
| delete | d | Delete breakpoint | d 1, delete |
| disable/enable | dis/en | Toggle breakpoint | disable 2 |
| Stepping | |||
| next | n | Step over (don't enter functions) | n |
| step | s | Step into functions | s |
| finish | fin | Run until function returns | finish |
| until | u | Run until line number | until 50 |
| Examining | |||
| p | Print variable value | p x, p *arr@5 | |
| display | disp | Auto-print after each step | display x |
| info locals | i lo | Show all local variables | info locals |
| backtrace | bt | Show call stack | bt, bt full |
| list | l | Show source code | list, l 10 |
06Complete Debugging Session (Step by Step)
Let's debug a buggy program from start to finish. This program has an off-by-one error:
1#include <stdio.h>23int sum_array(int arr[], int size) {4 int sum = 0;5 for (int i = 0; i <= size; i++) { // BUG: should be i < size6 sum += arr[i];7 }8 return sum;9}1011int main() {12 int numbers[] = {10, 20, 30, 40, 50};13 int result = sum_array(numbers, 5);14 printf("Sum = %d\n", result); // Prints wrong value!15 return 0;16}Step 1: Compile and Start GDB
# Compile with debug symbols
$ gcc -g -Wall buggy.c -o buggy
# Start GDB
$ gdb ./buggy
GNU gdb (Ubuntu 12.1) ...
Reading symbols from ./buggy...
(gdb)
Step 2: Set Breakpoint and Run
(gdb) break sum_array
Breakpoint 1 at 0x1169: file buggy.c, line 4.
(gdb) run
Starting program: ./buggy
Breakpoint 1, sum_array (arr=0x7ffd..., size=5) at buggy.c:4
4 int sum = 0;
Step 3: Examine the Code
(gdb) list
1 #include <stdio.h>
2
3 int sum_array(int arr[], int size) {
4 int sum = 0;
5 for (int i = 0; i <= size; i++) {
6 sum += arr[i];
7 }
8 return sum;
9 }
Step 4: Step Through and Inspect
# Check function parameters
(gdb) print size
$1 = 5
# Print the array (5 elements starting at arr)
(gdb) print *arr@5
$2 = {10, 20, 30, 40, 50}
# Set a conditional breakpoint: stop when i reaches 5
(gdb) break 6 if i == 5
Breakpoint 2 at 0x1180: file buggy.c, line 6.
(gdb) continue
Breakpoint 2, sum_array (arr=..., size=5) at buggy.c:6
6 sum += arr[i];
# Check the index — it's 5, but array only has indices 0-4!
(gdb) print i
$3 = 5
# What value is at arr[5]? Garbage!
(gdb) print arr[5]
$4 = 32767
Bug Found!
The loop condition i <= size should be i < size. With size=5, the loop runs when i=5, accessing arr[5] which is out of bounds (indices 0-4 only)!
07Breakpoint Techniques
Location Breakpoints
b main — at function
b 25 — at line 25
b file.c:42 — specific file
b +5 — 5 lines ahead
Conditional Breakpoints
b 10 if i == 5
b foo if x > 100
b 20 if strcmp(s,"test")==0
condition 1 i == 10 — modify existing
Watchpoints
watch x — break when x changes
watch *0x1234 — watch memory address
rwatch x — break when x is read
awatch x — break on read or write
Managing Breakpoints
info break — list all
disable 2 — temporarily disable
enable 2 — re-enable
delete 2 — remove breakpoint
clear main — clear by location
08Examining Variables & Memory
Print Formats
print x — decimal (default)
print/x x — hexadecimal → 0x2a
print/t x — binary → 101010
print/o x — octal → 052
print/c x — character → '*'
print/f x — float
print/s str — string
Arrays and Pointers
print *arr@10 — print 10 elements
print arr[0]@5 — elements 0-4
print (char*)ptr — cast and print
print sizeof(arr) — get size
Structures
print student — entire struct
print student.name — member access
print ptr->age — pointer member
ptype student — show struct definition
Memory Examination
# x/[count][format][size] address
x/10xb &arr — 10 bytes in hex
x/4xw &x — 4 words (32-bit) in hex
x/s str — as string
x/10i main — 10 instructions
Useful Info Commands
info locals — all local variables
info args — function arguments
info registers — CPU registers
info frame — current stack frame
09Debugging Segmentation Faults
When your program crashes with a segfault (SIGSEGV), GDB helps you find exactly where and why:
1#include <stdio.h>23int main() {4 int *ptr = NULL;5 *ptr = 42; // CRASH: Dereferencing NULL pointer6 return 0;7}$ gcc -g crash.c -o crash
$ gdb ./crash
(gdb) run
Starting program: ./crash
Program received signal SIGSEGV, Segmentation fault.
0x0000555555555149 in main () at crash.c:5
5 *ptr = 42;
# Where did it crash?
(gdb) backtrace
#0 0x0000555555555149 in main () at crash.c:5
# What's the value of ptr?
(gdb) print ptr
$1 = (int *) 0x0
# ptr is NULL (0x0) — that's the bug!
Analyzing Core Dumps
# Enable core dumps (Linux)
$ ulimit -c unlimited
# Run the crashing program
$ ./crash
Segmentation fault (core dumped)
# Debug the core file
$ gdb ./crash core
# Or on some systems:
$ gdb ./crash /var/lib/apport/coredump/core.*
10GDB Text User Interface (TUI)
GDB's TUI mode shows source code in a split window while you debug — much easier than typing list repeatedly!
gdb -tui ./program — start with TUI
tui enable — enable inside GDB
tui disable — disable TUI
Ctrl+x, a — toggle TUI on/off
Ctrl+x, 2 — two windows
layout src — source layout
layout asm — assembly layout
layout split — source + assembly
layout regs — show registers
focus cmd — focus command window
focus src — focus source window
TUI Layout Preview
┌──source────────────────────────────────────────┐
│ 3 int sum_array(int arr[], int size) { │
│ 4 int sum = 0; │
│B+>5 for (int i = 0; i < size; i++) { │
│ 6 sum += arr[i]; │
│ 7 } │
│ 8 return sum; │
│ 9 } │
└────────────────────────────────────────────────┘
(gdb) n
B+> shows current line with breakpoint
11Pro Tips & Tricks
Keyboard Shortcuts
Enter— repeat last commandCtrl+C— interrupt running programCtrl+L— refresh screen (TUI)Ctrl+P/N— previous/next command
.gdbinit File
Create ~/.gdbinit for custom settings:
set print pretty on
set pagination off
set history save on
Reverse Debugging
record — start recording
reverse-next — step backwards!
reverse-continue — run backwards
Python Scripting
GDB has Python support for automation:
(gdb) python print(gdb.parse_and_eval("x"))
12Quick Reference Card
Running
- run — start
- run args — with args
- continue — resume
- kill — stop
- quit — exit
Breakpoints
- break main
- break 25
- break f if x==5
- watch var
- delete 1
Stepping
- next — step over
- step — step into
- finish — to return
- until 30 — to line
Examining
- print x
- print *arr@10
- info locals
- backtrace
- list
!Code Pitfalls: Common Mistakes & What to Watch For
copied code often works in simple cases but contains subtle bugs that require deep debugging skills to find.
When you copy copied C code and it crashes, you're left debugging code you didn't write and don't fully understand. Copied code might produce code with off-by-one errors, memory leaks, or undefined behavior that passes superficial testing but fails with specific inputs. Without GDB skills, you'll be stuck.
The Trap: Copied code can generate code faster than you can understand it. When bugs appear, you need debugger skills to trace through unfamiliar logic. Copied code won't tell you *why* its code crashed or what the correct fix is — it might even suggest fixes that introduce new bugs.
The Reality: GDB is essential for working with any C code, especially copied code. Learn to set breakpoints, step through execution, and inspect variables. When copied code fails, run, backtrace, and print are your best friends for understanding what actually went wrong.
14Frequently Asked Questions
Q:Why does GDB say "No symbol table"?
A: You compiled without debug symbols. Add -g flag:gcc -g program.c -o program. Also avoid -O2 or higher optimization as it can confuse the debugger.
Q:How do I debug a program that takes command-line arguments?
A: Pass them to run:run arg1 arg2 arg3. Or set them once with set args arg1 arg2, then just use run repeatedly.
Q:Can I change variable values during debugging?
A: Yes! Useset variable x = 42 or justset x = 42. This lets you test different scenarios without recompiling or restarting.
Q:My program runs fine in GDB but crashes normally — why?
A: GDB initializes memory differently than normal execution. You likely have an uninitialized variable or memory bug. Use Valgrind or AddressSanitizer to find it.
14Related Debugging Tools
Valgrind
Finds memory leaks, uninitialized variables, and buffer overflows
valgrind --leak-check=full ./program
AddressSanitizer
Fast runtime memory error detector (compile-time)
gcc -fsanitize=address -g program.c
LLDB
Modern debugger, default on macOS. See our LLDB tutorial!
lldb ./program
IDE Debuggers
Visual debugging with GDB/LLDB backend
- • VS Code (C/C++ extension)
- • CLion (JetBrains)
- • Eclipse CDT
Test Your Knowledge
Related Tutorials
Debugging with LLDB
Master LLDB, the modern debugger for macOS and Linux! Learn installation, essential commands, and how to transition from GDB. Perfect for Xcode and Clang users.
Memory Debugging Tools
Find memory bugs with professional tools! Learn Valgrind, AddressSanitizer (ASan), and how to detect memory leaks, buffer overflows, and use-after-free errors.
Makefiles and Build Automation
Automate your build process with Makefiles! Learn to compile multi-file projects with a single command, use variables, and create professional build systems.
Have Feedback?
Found something missing or have ideas to improve this tutorial? Let us know on GitHub!