Coming Soon
This lesson is currently being developed
Using an integrated debugger: Running and breakpoints
Master using IDE debugging tools effectively.
What to Expect
Comprehensive explanations with practical examples
Interactive coding exercises to practice concepts
Knowledge quiz to test your understanding
Step-by-step guidance for beginners
Development Status
Content is being carefully crafted to provide the best learning experience
Preview
Early Preview Content
This content is still being developed and may change before publication.
3.7 — Using an integrated debugger: Running and breakpoints
In this lesson, you'll learn how to effectively use breakpoints to control program execution and examine your code at specific points. Breakpoints are one of the most fundamental and powerful features of integrated debuggers.
What are breakpoints?
A breakpoint is a intentional stopping point in your program that pauses execution when reached. When your program hits a breakpoint:
- Execution pauses before that line runs
- You can examine variable values
- You can step through code from that point
- You can continue execution or set more breakpoints
Think of breakpoints as "pause buttons" strategically placed in your code.
Types of breakpoints
Line breakpoints
The most common type - stops execution at a specific line.
#include <iostream>
int calculateSum(int n)
{
int sum = 0; // Breakpoint 1: Check initial state
for (int i = 1; i <= n; ++i)
{
sum += i; // Breakpoint 2: Watch sum change
std::cout << "i=" << i << ", sum=" << sum << std::endl;
}
return sum; // Breakpoint 3: Check final result
}
int main()
{
std::cout << "Starting program" << std::endl; // Breakpoint 4: Program entry
int result = calculateSum(5);
std::cout << "Result: " << result << std::endl;
return 0; // Breakpoint 5: Program exit
}
/*
Typical breakpoint placement strategy:
- Breakpoint 4: Verify program starts correctly
- Breakpoint 1: Check function parameters and initial values
- Breakpoint 2: Monitor loop behavior
- Breakpoint 3: Verify function completes correctly
- Breakpoint 5: Confirm program finishes properly
*/
Conditional breakpoints
Stop only when a specific condition is true.
#include <iostream>
#include <vector>
void demonstrateConditionalBreakpoints()
{
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (size_t i = 0; i < numbers.size(); ++i)
{
int square = numbers[i] * numbers[i];
// Conditional breakpoint here: i == 7
// This will only break when i equals 7
std::cout << "numbers[" << i << "]² = " << square << std::endl;
if (square > 50)
{
// Regular breakpoint here to catch large squares
std::cout << "Large square detected: " << square << std::endl;
}
}
}
/*
Setting conditional breakpoints:
1. Set regular breakpoint on the line
2. Right-click → Edit Breakpoint
3. Add condition: i == 7
4. Breakpoint will only trigger when condition is true
This is extremely useful for:
- Large loops where you want to stop at specific iterations
- Functions called many times but you only care about certain cases
- Complex conditions that would be tedious to check manually
*/
Function breakpoints
Stop whenever a specific function is called.
#include <iostream>
// Function we want to monitor
int divide(int a, int b)
{
// Function breakpoint will stop here every time divide() is called
if (b == 0)
{
std::cout << "Division by zero!" << std::endl;
return 0;
}
return a / b;
}
void demonstrateFunctionBreakpoints()
{
std::cout << "=== Function Breakpoint Demo ===" << std::endl;
// All these calls will trigger the function breakpoint
int result1 = divide(10, 2); // Normal case
int result2 = divide(15, 3); // Normal case
int result3 = divide(8, 0); // Error case - this is what we're looking for
int result4 = divide(20, 4); // Normal case
std::cout << "Results: " << result1 << ", " << result2 << ", "
<< result3 << ", " << result4 << std::endl;
}
/*
Setting function breakpoints:
1. In many IDEs: Debug → New Breakpoint → Function Breakpoint
2. Enter function name: divide
3. Breakpoint triggers on every call to divide()
Useful for:
- Functions called from many different places
- Tracking down unexpected function calls
- Monitoring library functions
- Finding when functions are called with problematic parameters
*/
Strategic breakpoint placement
The sandwich approach
Place breakpoints before and after suspicious code sections.
#include <iostream>
#include <vector>
int findSecondLargest(std::vector<int>& arr)
{
// BEFORE: Check input
std::cout << "Input size: " << arr.size() << std::endl; // Breakpoint A
if (arr.size() < 2)
return -1;
// Sort the array (suspicious section)
for (size_t i = 0; i < arr.size() - 1; ++i) // Breakpoint B: Before sort
{
for (size_t j = 0; j < arr.size() - 1 - i; ++j)
{
if (arr[j] > arr[j + 1])
{
std::swap(arr[j], arr[j + 1]);
}
}
}
// AFTER: Check sorting result
std::cout << "After sorting: "; // Breakpoint C: After sort
for (int x : arr) std::cout << x << " ";
std::cout << std::endl;
return arr[arr.size() - 2]; // Second largest
}
/*
Sandwich debugging strategy:
- Breakpoint A: Verify inputs are correct
- Breakpoint B: State before suspicious operation
- Breakpoint C: State after suspicious operation
This helps isolate exactly where problems occur.
*/
Binary search breakpoint placement
For large codebases, use breakpoints to narrow down problem areas.
#include <iostream>
void processLargeWorkflow()
{
std::cout << "Starting large workflow..." << std::endl;
// Section 1: Data preparation
std::cout << "Phase 1: Data preparation" << std::endl; // Breakpoint 1
// ... lots of code ...
std::cout << "Phase 1 complete" << std::endl;
// Section 2: Data validation
std::cout << "Phase 2: Data validation" << std::endl; // Breakpoint 2
// ... lots of code ...
std::cout << "Phase 2 complete" << std::endl;
// Section 3: Data processing (bug might be here)
std::cout << "Phase 3: Data processing" << std::endl; // Breakpoint 3
// ... lots of code with potential bug ...
std::cout << "Phase 3 complete" << std::endl;
// Section 4: Output generation
std::cout << "Phase 4: Output generation" << std::endl; // Breakpoint 4
// ... lots of code ...
std::cout << "Phase 4 complete" << std::endl;
}
/*
Binary search with breakpoints:
1. Set breakpoints at major section boundaries
2. Run program - which section has the bug?
3. Remove breakpoints from good sections
4. Add breakpoints within the problematic section
5. Repeat until you isolate the exact problem
*/
Using the debugger run controls
Continue (F5)
Resumes execution until next breakpoint or program end.
#include <iostream>
void demonstrateRunControls()
{
std::cout << "Point A" << std::endl; // Breakpoint 1
for (int i = 0; i < 5; ++i)
{
std::cout << "Loop iteration " << i << std::endl; // Breakpoint 2
}
std::cout << "Point B" << std::endl; // Breakpoint 3
if (true)
{
std::cout << "Inside if block" << std::endl; // Breakpoint 4
}
std::cout << "Point C" << std::endl; // Breakpoint 5
}
/*
Using Continue (F5):
1. Program stops at Breakpoint 1 (Point A)
2. Press F5 → continues to Breakpoint 2 (first loop iteration)
3. Press F5 → continues to Breakpoint 2 (second loop iteration)
4. Press F5 → continues to Breakpoint 2 (third loop iteration)
... and so on
This lets you jump between interesting points without stepping through every line.
*/
Restart debugging
Restart the entire debugging session from the beginning.
#include <iostream>
int globalCounter = 0; // This will reset when you restart debugging
void incrementCounter()
{
globalCounter++;
std::cout << "Counter: " << globalCounter << std::endl; // Breakpoint here
}
void demonstrateRestart()
{
std::cout << "=== Restart Demo ===" << std::endl;
incrementCounter(); // First call
incrementCounter(); // Second call
incrementCounter(); // Third call
std::cout << "Final counter: " << globalCounter << std::endl;
}
/*
Using Restart:
1. Run to breakpoint in incrementCounter()
2. Note globalCounter value
3. Use Restart (Ctrl+Shift+F5 in many IDEs)
4. Program starts fresh, globalCounter resets to 0
5. You can test different scenarios from the beginning
Useful when you want to:
- Test the same scenario multiple times
- Change input data and see different results
- Compare behavior across multiple runs
*/
Advanced breakpoint techniques
Exception breakpoints
Stop when exceptions are thrown.
#include <iostream>
#include <stdexcept>
void riskyFunction(int value)
{
if (value < 0)
{
throw std::invalid_argument("Negative values not allowed"); // Exception breakpoint here
}
if (value == 0)
{
throw std::runtime_error("Zero is not allowed"); // Exception breakpoint here
}
std::cout << "Processing value: " << value << std::endl;
}
void demonstrateExceptionBreakpoints()
{
int testValues[] = {5, -1, 0, 10};
for (int value : testValues)
{
try
{
riskyFunction(value);
}
catch (const std::exception& e)
{
std::cout << "Caught exception: " << e.what() << std::endl;
}
}
}
/*
Setting exception breakpoints:
1. Debug → Windows → Exception Settings (Visual Studio)
2. Check "C++ Exceptions" to break on all C++ exceptions
3. Or set specific exception types
When exception breakpoint triggers:
- Execution stops exactly where exception is thrown
- You can examine variables at the point of failure
- You can see the call stack that led to the exception
*/
Hit count breakpoints
Stop only after a breakpoint has been hit a certain number of times.
#include <iostream>
void processItem(int item)
{
// Hit count breakpoint: Stop on 5th hit
// This line will be hit 10 times, but debugger only stops on the 5th time
std::cout << "Processing item: " << item << std::endl;
// Some processing logic here
if (item % 2 == 0)
{
std::cout << "Even number: " << item << std::endl;
}
}
void demonstrateHitCountBreakpoints()
{
std::cout << "=== Hit Count Breakpoint Demo ===" << std::endl;
for (int i = 1; i <= 10; ++i)
{
processItem(i);
}
}
/*
Setting hit count breakpoints:
1. Set regular breakpoint
2. Right-click → Edit Breakpoint
3. Set condition type to "Hit Count"
4. Choose options:
- "Break always" (normal breakpoint)
- "Break when hit count equals X"
- "Break when hit count is multiple of X"
- "Break when hit count is greater than or equal to X"
Useful for:
- Functions called many times but you only care about later calls
- Loop iterations where problem occurs after many iterations
- Intermittent bugs that appear after certain conditions
*/
Breakpoint management
Disabling vs. deleting breakpoints
#include <iostream>
void demonstrateBreakpointManagement()
{
std::cout << "Function start" << std::endl; // Breakpoint A (active)
for (int i = 0; i < 3; ++i)
{
std::cout << "Loop " << i << std::endl; // Breakpoint B (disabled for now)
if (i == 1)
{
std::cout << "Special case" << std::endl; // Breakpoint C (conditional)
}
}
std::cout << "Function end" << std::endl; // Breakpoint D (active)
}
/*
Breakpoint management strategies:
DISABLE (don't delete) when:
- You might need the breakpoint again later
- You're testing different scenarios
- You want to temporarily skip certain stops
DELETE when:
- Breakpoint is no longer needed
- Code section is working correctly
- Cleaning up for final testing
CONDITIONAL breakpoints when:
- You only care about specific cases
- Normal breakpoint stops too often
- You're hunting for specific conditions
*/
Debugging workflow with breakpoints
Systematic debugging approach
#include <iostream>
#include <vector>
// Function with a bug - let's find it systematically
std::vector<int> filterEvenNumbers(const std::vector<int>& input)
{
std::vector<int> result;
std::cout << "Filtering " << input.size() << " numbers" << std::endl; // Breakpoint 1: Entry
for (size_t i = 0; i <= input.size(); ++i) // BUG: should be < not <=
{
// Breakpoint 2: Loop iteration (conditional: i >= input.size() - 2)
std::cout << "Checking index " << i << std::endl;
if (input[i] % 2 == 0) // This will crash on the last iteration
{
result.push_back(input[i]);
// Breakpoint 3: Adding even number
std::cout << "Added " << input[i] << " to result" << std::endl;
}
}
std::cout << "Filtered " << result.size() << " even numbers" << std::endl; // Breakpoint 4: Exit
return result;
}
void systematicDebuggingDemo()
{
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8};
std::cout << "=== Systematic Debugging Demo ===" << std::endl;
try
{
std::vector<int> evenNumbers = filterEvenNumbers(numbers);
std::cout << "Even numbers found: ";
for (int num : evenNumbers)
{
std::cout << num << " ";
}
std::cout << std::endl;
}
catch (const std::exception& e)
{
std::cout << "Exception caught: " << e.what() << std::endl;
}
}
/*
Systematic debugging workflow:
1. INITIAL SETUP
- Set breakpoint at function entry (Breakpoint 1)
- Run program and verify inputs are correct
2. IDENTIFY PROBLEM AREA
- Set breakpoint at suspected problem (loop condition)
- Use conditional breakpoint to catch edge case (i >= input.size() - 2)
3. ISOLATE THE BUG
- When conditional breakpoint hits, examine variables:
* i = 8, input.size() = 8
* About to access input[8] but valid indices are 0-7
- Bug found: loop condition should be i < input.size()
4. VERIFY THE FIX
- Fix the bug: change <= to <
- Keep breakpoints active and rerun
- Confirm program works correctly
5. CLEANUP
- Remove or disable breakpoints once bug is fixed
- Do final test run without debugger
*/
IDE-specific breakpoint features
Visual Studio Code
Setting breakpoints:
- Click in left margin (red dot appears)
- F9 to toggle breakpoint on current line
- Right-click in margin for breakpoint options
Breakpoint panel:
- View → Debug → Breakpoints
- Shows all breakpoints with file/line info
- Can enable/disable/delete from panel
Conditional breakpoints:
- Right-click breakpoint → Edit Breakpoint
- Add condition or hit count
Visual Studio
Setting breakpoints:
- Click in left margin (red circle appears)
- F9 to toggle breakpoint on current line
- Debug → Toggle Breakpoint
Advanced features:
- Right-click breakpoint for context menu
- Conditions, hit count, filters available
- Exception breakpoints in Exception Settings
Breakpoints window:
- Debug → Windows → Breakpoints (Ctrl+Alt+B)
- Manage all breakpoints from one location
Code::Blocks
Setting breakpoints:
- Click in left margin (red circle appears)
- F5 to toggle breakpoint on current line
- Debug → Toggle breakpoint
Managing breakpoints:
- Debug → Debugging windows → Breakpoints
- View and manage all breakpoints
- Enable/disable without deleting
Best practices for breakpoints
- Start with few breakpoints: Don't overwhelm yourself with too many stops
- Use descriptive conditions: Make conditional breakpoints clear and specific
- Clean up regularly: Remove or disable breakpoints you no longer need
- Use the call stack: When breakpoint hits, examine how you got there
- Combine with watch variables: Monitor key variables at breakpoints
- Save breakpoint configurations: Some IDEs let you save breakpoint layouts
Summary
Breakpoints are essential tools for controlling program execution and examining code behavior:
Types of breakpoints:
- Line breakpoints: Stop at specific lines
- Conditional breakpoints: Stop only when conditions are met
- Function breakpoints: Stop when functions are called
- Exception breakpoints: Stop when exceptions are thrown
Strategic placement:
- Use the sandwich approach around suspicious code
- Apply binary search to narrow down problem areas
- Place breakpoints at function entry/exit points
- Set conditional breakpoints for specific scenarios
Run controls:
- Continue (F5): Resume until next breakpoint
- Restart: Begin debugging session over
- Stop: End debugging session
Effective breakpoint usage dramatically improves debugging efficiency by letting you focus on the exact moments when problems occur.
In the next lesson, you'll learn how to watch and examine variable values while debugging.
Quiz
- What's the difference between disabling and deleting a breakpoint?
- When would you use a conditional breakpoint instead of a regular breakpoint?
- How does the "sandwich approach" help with debugging?
- What is a function breakpoint and when is it useful?
- How can hit count breakpoints help with intermittent bugs?
Practice exercises
-
Set up systematic breakpoints for this buggy function and find the error:
int calculateAverage(const std::vector<int>& numbers) { int sum = 0; for (int i = 0; i <= numbers.size(); ++i) // Bug here { sum += numbers[i]; } return sum / numbers.size(); }
-
Use conditional breakpoints to debug this function only when problematic input occurs:
bool isPalindrome(const std::string& word) { // Set conditional breakpoint for empty strings or single characters for (size_t i = 0; i < word.length() / 2; ++i) { if (word[i] != word[word.length() - 1 - i]) return false; } return true; }
-
Practice breakpoint management: Create a program with multiple functions, set various types of breakpoints, then practice enabling/disabling them strategically to debug different scenarios.
-
Use exception breakpoints to catch and examine this problematic code:
void processData(const std::vector<std::string>& data) { for (const auto& item : data) { int value = std::stoi(item); // May throw exception std::cout << value * 2 << std::endl; } }
Explore More Courses
Discover other available courses while this lesson is being prepared.
Browse CoursesLesson Discussion
Share your thoughts and questions