Debugging
3.1 — Syntax and Semantic Errors¶
Errors generally fall into two categories:
-
Syntax Errors: Occur when you break the "grammar" rules of C++ (e.g., missing a semicolon, mismatched braces). The compiler will catch these and refuse to build the program.
-
Semantic Errors: Occur when the code is syntactically correct, but it doesn't do what you intended (e.g., adding two numbers when you meant to subtract them). The compiler cannot catch these.
3.2 — The Debugging Process¶
Debugging follows a specific lifecycle:
-
Find the root cause: Identify exactly which line or logic is failing.
-
Understand the fix: Determine why the error happened and how to solve it without breaking other things.
-
Apply the fix: Change the code.
-
Retest: Verify the fix works and that no new bugs were introduced.
3.3 — A Strategy for Debugging¶
When a program doesn't work, don't just stare at the code. Follow a strategy:
-
Reproduce the bug: Find the exact steps or inputs that cause the failure.
-
Locate the bug: Narrow down the code to the specific function or loop causing the issue.
-
Isolate the issue: Comment out unrelated code or create a "minimal reproducible example" to focus only on the problematic part.
3.4 & 3.5 — Basic and More Debugging Tactics¶
If you don't have a sophisticated debugger, you can use these "manual" methods:
-
Comment out code: Temporarily disable sections of code to see if the bug persists.
-
Print statements: Insert
std::coutstatements to track variable values and see which parts of the code are actually executing.- Tip: Use
std::cerrfor error output as it is unbuffered and appears immediately.
- Tip: Use
-
Logging: Writing status information to a file to analyze after the program runs.
3.6 — Integrated Debugger: Stepping¶
Most IDEs (Visual Studio, VS Code, CLion) have a built-in Debugger. Stepping allows you to execute code one line at a time.
-
Step Into: Enters the function on the current line.
-
Step Over: Executes the current line (including functions) and moves to the next line in the current function.
-
Step Out: Finishes the current function and returns to the caller.
3.7 — Integrated Debugger: Running and Breakpoints¶
-
Breakpoints: A marker you set on a line of code. When the program runs in debug mode, it will pause execution exactly at that line.
-
Continue / Resume: Tells the program to keep running normally until it hits the next breakpoint or finishes.
-
Run to Cursor: Runs the program until it reaches the line where your mouse cursor currently sits.
3.8 — Integrated Debugger: Watching Variables¶
While the program is paused at a breakpoint, you can inspect the state of the system:
-
Variable Watch: A window that shows the current value of specific variables.
-
Locals Window: Automatically shows all variables defined in the current scope.
-
Tooltip Inspection: Hovering your mouse over a variable in the code editor to see its current value.
3.9 — Integrated Debugger: The Call Stack¶
The Call Stack is a list of all active functions that have been called but haven't finished yet.
-
It shows you the "path" the program took to get to the current line.
-
If your program crashes in a deep sub-function, the call stack helps you find which function originally started the chain of events.
3.10 — Finding Issues Before They Become Problems¶
Prevention is better than a cure.
-
Compiler Warnings: Turn your warning levels to the highest setting (
-Wall -Wextraon GCC/Clang or/W4on MSVC). Treat warnings as errors. -
Static Analysis: Tools that scan your code for common patterns that lead to bugs.
-
Unit Testing: Writing small scripts that automatically test your functions with various inputs to ensure they always return the expected result.