Inevitably, our program will run, but it will not work as we intended. Some boolean we thought would be true
will be false
, or some number will be different from what we expected it to be.
When this happens, and we don't understand why, our first task will be to try to get some observability on what is happening at run time.
We can think of programming and software design as having two "phases" - build time, and run time.
Build time happens when we press the "build" button in our editor. It covers all the processes that are involved in compiling and packaging our software into a product, ready to send to users.
Run time is when when we, or our users, later run our software.
In the context of bugs and errors, an example of a build time issue is something that prevents our software from compiling at all. You may already have seen some of those - perhaps a missing semicolon, or some other misuse of syntax.
However, just because our code builds successfully, that doesn't mean it's correct. There may still be run-time errors. Run time errors are situations where our software will crash, or not do what we expected whenever someone is running the program.
We already know one way to get observability on our program as it runs - we can scatter statements that write to cout
throughout our code to see the state of variables through time.
But that can take a lot of effort, particularly as our program gets larger. There is a more efficient way: using a debugger.
A debugger is a tool used to inspect and modify the state of a running program.
It allows us to pause execution (often at specific points known as breakpoints), step through code, inspect variables, and even change their values on the fly.
Think of it as a magnifying glass that lets us zoom in on our code while it's running, providing valuable insights into its behavior.
Most IDEs have debuggers built-in, and in this lesson we’ll be using screenshots from Visual Studio, but all debuggers tend to work in very similar ways. Their features include:
Right now, our programs are very simple. However, getting comfortable with a debugger not only helps us find bugs - it’s also a great learning tool.
It helps us quickly understand what our program is doing, so it's helpful to get into the habit early. Debugging a simple program is a good first step, so let's get started by creating some breakpoints.
Let's create a really simple program to see how a debugger can help us. There are no new concepts here - we're just creating and updating some variables. These are concepts we've already seen:
#include <iostream>
using namespace std;
int Health { 100 };
bool isDead { false };
int main()
{
Health -= 10;
isDead = Health <= 0;
Health -= 200;
isDead = Health <= 0;
}
Debugging involves the introduction of "breakpoints" to our code. The debugger will run our program until it reaches a breakpoint, at which point it will pause, and allow us to examine the state of our running application at that specific time.
In most editors, we set a breakpoint by clicking an area to the left of the line of code where we want execution to pause. This should create some visual indicator to show where our breakpoint is.
In Visual Studio and many other editors, it appears as a red circle, as shown to the left of line 9 below.
With our breakpoint set, we can then tell our editor to start the debugger. In Visual Studio, this is the large "Local Windows Debugger" button on our top menu.
When our debugger runs, it should pause at the breakpoint we earlier set. The user interface of our IDE will also often change when we start debugging our program.
In Visual Studio, for example, we can now hover our pointer over any variable name to see its current value.
In the below screenshot, Health
still has its initial value of 100
. The line of code our debugger has paused at will reduce Health
by 10
, but that line hasn't run yet.
To control the execution of our program when debugging, we should see some new UI elements. We'll discuss these more as we cover more advanced topics, but for now, the most important commands are Step Over
and Continue
In Visual Studio, these appear on our top toolbar and are also accessible from the "Debug" menu.
Step Over will cause the debugger to execute the next line of code. In our situation, by pressing Step Over, we will execute line 9, and pause before line 10.
Executing line 9 will decrease Health
by 10
so, as expected, Health
now has a value of 90
after pressing Step Over once.
Continue will instruct the debugger to continue executing our program until it reaches the next breakpoint. If we have no further breakpoints, it will continue to completion.
Debuggers generally will not let us move backward. However, we can just restart from scratch if we advance too far.
In this lesson, we've taken a significant step in understanding the essential tools for C++ programming, specifically focusing on the use of a debugger. Here’s a quick recap of what we covered:
With our initial exploration of variables, maths, and booleans out of the way, it's time to move on to the next chapter! In the next lesson, we will delve into another fundamental aspect of C++ programming: creating and calling functions.
This next step will enhance your understanding of how to structure and organize your code efficiently. Key topics will include:
Creating a tiny program using numbers and booleans, then adding some breakpoints so we can step through our code in a debugger.
Become a software engineer with C++. Starting from the basics, we guide you step by step along the way