C++ Debugging Practical Complete Guide | gdb, LLDB, Visual Studio Complete Usage
이 글의 핵심
Organizes core concepts and practical points of C++ debugging practical guide.
1. cout Debugging
Below is an implementation example using C++. Import the necessary modules. Understand the role of each part while examining the code.
#include <iostream>
using namespace std;
int buggyFunction(int x) {
cout << "[DEBUG] buggyFunction called, x = " << x << endl;
int result = x * 2;
cout << "[DEBUG] result = " << result << endl;
return result;
}
Pros: Simple, fast
Cons: Requires code modification, performance impact
2. Check Conditions with assert
Below is an implementation example using C++. Import the necessary modules. Understand the role of each part while examining the code.
#include <cassert>
void process(int* ptr) {
assert(ptr != nullptr); // Check only in debug build
assert(*ptr > 0);
// ...
}
int main() {
int* ptr = nullptr;
process(ptr); // Assertion fails!
}
3. Using Breakpoints
Visual Studio
Below is an implementation example using code. Try running the code directly to check its operation.
F9: Set/unset breakpoint
F5: Start debugging
F10: Step over (execute line by line)
F11: Step into (enter function)
Shift+F11: Step out (exit function)
GDB
Here is detailed implementation code using bash. Understand the role of each part while examining the code.
# Compile (with -g option required)
g++ -g program.cpp -o program
# Run gdb
gdb ./program
# Set breakpoint
(gdb) break main
(gdb) break file.cpp:42
# Run
(gdb) run
# Step by step
(gdb) next # Step Over
(gdb) step # Step Into
# Print variable
(gdb) print variable
(gdb) print *ptr
# Continue execution
(gdb) continue
4. Conditional Breakpoints
Visual Studio
Right-click breakpoint → Conditions
Example: i == 100
GDB
(gdb) break main if x == 10
(gdb) condition 1 i == 100 # Add condition to breakpoint 1
5. Memory Debugging
Valgrind (Linux)
Below is an implementation example using bash. Understand the role of each part while examining the code.
# Install
sudo apt install valgrind
# Check memory leaks
valgrind --leak-check=full ./program
# Output example
==12345== HEAP SUMMARY:
==12345== in use at exit: 40 bytes in 1 blocks
==12345== total heap usage: 1 allocs, 0 frees, 40 bytes allocated
==12345==
==12345== LEAK SUMMARY:
==12345== definitely lost: 40 bytes in 1 blocks
AddressSanitizer
Below is an implementation example using bash. Try running the code directly to check its operation.
# Compile
g++ -fsanitize=address -g program.cpp -o program
# Run
./program
# Auto-detect errors
=================================================================
==12345==ERROR: AddressSanitizer: heap-use-after-free
Understanding with everyday analogy: Think of memory as an apartment building. Stack is like an elevator - fast but limited space. Heap is like a warehouse - spacious but takes time to find things. Pointers are like notes saying “Floor 3, Unit 302” - they point to addresses.
Practical Examples
Example 1: Debugging Segmentation Fault
Here is detailed implementation code using C++. Import the necessary modules and perform branching with conditionals. Understand the role of each part while examining the code.
#include <iostream>
using namespace std;
int main() {
int* ptr = nullptr;
// Debugging tip: Check nullptr
if (ptr == nullptr) {
cerr << "Error: ptr is nullptr!" << endl;
return 1;
}
*ptr = 10; // Segfault!
return 0;
}
Debugging with GDB: Below is an implementation example using bash. Try running the code directly to check its operation.
$ gdb ./program
(gdb) run
Program received signal SIGSEGV, Segmentation fault.
(gdb) backtrace # Check call stack
(gdb) print ptr # Check ptr value
$1 = (int *) 0x0
Example 2: Debugging Infinite Loop
Below is an implementation example using C++. Import the necessary modules and process data with loops. Understand the role of each part while examining the code.
#include <iostream>
using namespace std;
int main() {
int i = 0;
while (i < 10) {
cout << i << endl;
// i++; Missing! Infinite loop
}
return 0;
}
Debugging method:
- Stop with Ctrl+C
- Set breakpoint
- Check variable i value
- Check if i++ executes
Example 3: Log Macro
Here is detailed implementation code using C++. Import the necessary modules. Understand the role of each part while examining the code.
#include <iostream>
using namespace std;
#ifdef DEBUG
#define LOG(msg) cout << "[" << __FILE__ << ":" << __LINE__ << "] " << msg << endl
#else
#define LOG(msg)
#endif
int main() {
int x = 10;
LOG("x = " << x);
x *= 2;
LOG("x after multiply = " << x);
return 0;
}
Compile:
# Debug build
g++ -DDEBUG program.cpp -o program
# Release build (no logs)
g++ program.cpp -o program
Summary
Key Points
- cout debugging: Simple but requires code modification
- assert: Check conditions in debug build
- Breakpoints: Pause execution and inspect state
- Memory debugging: Use Valgrind or AddressSanitizer
- Conditional breakpoints: Break only when condition met
Debugging Tools
✅ Visual Studio:
- Integrated debugger
- Easy to use
- Windows-friendly ✅ GDB/LLDB:
- Command-line debuggers
- Powerful and flexible
- Linux/Mac standard ✅ AddressSanitizer:
- Fast memory error detection
- Compile-time instrumentation
- Recommended for daily use
Best Practices
- ✅ Compile with debug symbols (-g)
- ✅ Use assertions for preconditions
- ✅ Learn debugger keyboard shortcuts
- ❌ Don’t leave debug code in production
- ❌ Don’t ignore warnings
Related Articles
- C++ Debugger gdb/LLDB
- C++ Template Basics
- C++ Function Basics Master debugging for efficient C++ development! 🚀