C++
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C++
-
Section 4: Memory Management
-
Section 5: Templates and Generic Programming
-
Section 6: The Standard Template Library
-
Section 7: Modern C++ Features
-
Section 8: Error Handling
-
Section 9: Multithreading
-
Section 10: Operator Overloading and Type Conversion
-
Section 11: Advanced Topics
-
Section 12: Tooling and Build Systems
-
Section 13: Design Patterns in C++
-
Section 14: Interfacing with C and Systems Programming
-
Section 15: Networking and IPC Basics
-
Section 16: Graphics and Game Programming Basics
-
Section 17: The Boost Libraries
-
Section 18: Data Structures and Algorithms in C++
-
Section 19: Practical Projects
-
Section 20: More Concurrency Patterns
-
Section 21: More OOP and Design Practice
-
Section 22: File I/O and Streams
-
Section 23: More Standard Library
-
Section 24: Practice Exercises
-
Section 25: Interview and Algorithm Practice
-
Section 26: Compiler and Language Internals
-
Section 27: GUI and Application Frameworks Overview
-
Section 28: Testing and Quality Practices
-
Section 29: Numerics Library
-
Section 30: Concepts Library (C++20)
-
Section 31: Ranges Library (C++20) In Depth
-
Section 32: More Utility Library
131: Debugging with GDB
How do I actually get GDB to see my variables and line numbers?
If you just run gdb ./my_program, you're going to be disappointed. You'll see a bunch of hex addresses and "no debugging symbols found," which is basically GDB telling you it has no idea how the binary maps back to your source code. To fix this, you have to tell the compiler to include those symbols.
You need the -g flag. I usually just make it a habit to include it in my Makefile or build script during development. Here is how you'd compile a simple program for debugging:
g++ -g main.cpp -o my_program
Keep in mind that -g increases the size of your binary because it's storing a map of every variable and line number. You'll want to strip these out or switch to -O2 or -O3 optimization flags when you're ready for production, but for now, -g is your best friend.
My program just crashed with "Segmentation Fault"—how do I find the exact line?
This is where GDB really earns its keep. Instead of peppering your code with std::cout << "Got here" << std::endl; (which is tedious and honestly a bit primitive), you can let the debugger catch the crash in real-time.
Let's say you're working with a linked list and you forgot to check if a pointer was null before dereferencing it:
struct Node {
int data;
Node* next;
};
void printList(Node* head) {
while (head != nullptr) {
std::cout << head->data << " ";
head = head->next;
}
}
int main() {
Node* head = nullptr;
printList(head); // This is fine
Node* badNode = nullptr;
std::cout << badNode->data; // CRASH!
}
To find the crash, load the program, type run, and wait for the segfault. Once it hits, type backtrace (or just bt). GDB will print the call stack, showing you the exact file and line number where the crash occurred. It’s an immediate "aha!" moment.
Can I move through the code line-by-line and see what's happening inside a loop?
Absolutely. You don't have to just wait for a crash; you can pause time. You do this using breakpoints. If you want the program to stop at the start of your printList function, you'd type break printList or break main.cpp:10.
Once the program hits that breakpoint, you have a few essential commands:
n(next): Executes the current line and moves to the next one. It treats function calls as a single step.s(step): If the current line is a function call, this "steps into" that function so you can debug it internally.p variable_name(print): This is the gold mine. You can print the current value of any variable in scope.p headwill show you the address, andp head->datawill show you the actual value.
I personally find display variable_name even more useful than print. While print is a one-time request, display tells GDB to show you the value of that variable every single time the program stops. It's great for watching a loop counter or a pointer move through a list.
What's the real difference between 'next' and 'step'?
This trips up a lot of people early on. Think of next as "skip over the details" and step as "dive into the details."
If you are on a line that calls std::sort() and you hit n, GDB will execute the entire sorting algorithm and stop at the very next line in your current function. If you hit s, GDB will jump you inside the std::sort implementation in the C++ Standard Library.
Fair warning: if you step into a standard library function, you're going to see a lot of complex, template-heavy code that is probably not what you're looking for. When that happens, just use finish. That command tells GDB to execute the rest of the current function and return you back to the caller.
📋 Practical Task
Hunting the Null Pointer in a Doubly Linked List
You have been given a snippet of a Doubly Linked List implementation where the removeNode function is causing a segmentation fault when trying to delete the tail of the list. Your goal is to use GDB to identify the exact line of the crash and the state of the pointers at that moment.
# save as debug_me.cpp
# Compile with: g++ -g debug_me.cpp -o debug_me
#include <iostream>
struct Node {
int val;
Node *prev, *next;
Node(int v) : val(v), prev(nullptr), next(nullptr) {}
};
void removeNode(Node* node, Node*& head, Node*& tail) {
if (node->prev) {
node->prev->next = node->next;
} else {
head = node->next;
}
if (node->next) {
node->next->prev = node->prev;
} else {
// The bug is lurking around here...
tail = node->prev;
std::cout << "Removing tail: " << node->next->val << std::endl;
}
delete node;
}
int main() {
Node* head = new Node(1);
Node* tail = new Node(2);
head->next = tail;
tail->prev = head;
removeNode(tail, head, tail);
return 0;
}
Your task:
- Compile the code with debugging symbols.
- Run the program inside GDB.
- When the program crashes, use
backtraceto find the line number. - Use
print node->nextto verify why the crash is happening. - Fix the code so that it can safely remove the tail node without crashing.
There are no comments for now.