Skip to content

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:

  1. Find the root cause: Identify exactly which line or logic is failing.

  2. Understand the fix: Determine why the error happened and how to solve it without breaking other things.

  3. Apply the fix: Change the code.

  4. 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::cout statements to track variable values and see which parts of the code are actually executing.

    • Tip: Use std::cerr for error output as it is unbuffered and appears immediately.
  • 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 -Wextra on GCC/Clang or /W4 on 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.