本文以CodeBlocks为例,简述C/C++程序断点调试的基本方法和过程。其它的IDE环境,大同小异。

1. 简介

在程序有BUG/缺陷,需要修复时,断点调试是发现缺陷的重要方法。对于初学者而言,断点调试还有另外一个用途:帮助理解程序背后的逻辑。

本文以CodeBlocks 20.03为例,简述C/C++程序断点调试的基本方法和过程。其它的IDE环境,大同小异。

版权声明

本文可以在互联网上自由转载,但必须:注明出处(作者:海洋饼干叔叔)并包含指向本页面的链接。

本文不可以以纸质出版为目的进行改编、摘抄。


在进行后续调试试验前,需要先对CodeBlocks的C/C++编译开发环境进行安装和配置。详细过程请参见下述链接:http://codelearn.club/2022/11/codeblocks/

2. 程序代码

在CodeBlocks中新建一个Console Application(C++), 项目名为DebugExample。

image-20221212201126880

将项目下的main.cpp打开,录入下述代码(粘贴复制)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//Project - DebugExample
#include <stdio.h>
#include <stdbool.h>

bool isPrime(int n) { //函数的定义
if (n<=1)
return false;
for (int i=2;i<n;i++)
if (n % i == 0)
return false;

return true;
}

int main(){
printf("Try to find all prime number(<=10):\n");

int iFound = 0; //发现的质数个数
for (int i=2;
i<=10;
i++)
{
if (!isPrime(i))
continue;
iFound++;
printf("%d, ",i);
}

printf("\n%d prime numbers been found.",iFound);
return 0;
}

image-20221212201552610

上述程序的用途在于找出2 ~ 10之间的全部质数并打印出来。请读者注意第19 ~ 21行,for循环的初始化语句,测试表达式,更新表达式故意分作了三行,以便于观察。

3. 断点

image-20221212201836982

在第18行代码的行号左边单击鼠标,便会出现如图所示的红色断点(break point),在相同位置再次单击,断点便会消失。可以给同一段程序添加多个断点。

4.调试

image-20221212202137223

选择菜单Build-Build and run, 尝试编译并运行程序。

image-20221212202212221

如果能到上述运行结果,说明程序编译正确,运行正常。

image-20221212201910887

程序运行无误且打好断点后,选择菜单Debug - Start/Continue,开始调试。

注意:必须要关闭前次运行的终端,即结束代码的前次运行。

image-20221212203229214

与正常的程序运行不动,程序并没有“一口气”从头执行到尾,而是暂停在了第18行的断点处。图中的黄色箭头表明程序的当前执行点,准确地说,箭头所指的行是接下来准备执行的代码行。

悬浮窗口Watches中显示了当前作用域中的自动变量。如图所见,iFound变量的当前值为0。如果读者没有看到上述Watches窗口,选择菜单项Debug-Debugging windows-Watches将其打开。

image-20221212203138801

image-20221212203640688

在CodeBlocks里有一个跟调试有条的工具条,如图所示,这些按钮依次是Continue, Run to cursor, Next line, Step into, Step out, Next instruction, Step into instruction, Break debugger, Stop debugger。

image-20221212203952839

点击Next line, 可见第18行被执行,黄色箭头来到第19行。读者可能注意到此时变量i的值为16, 这是正常的,因为for循环的初始化语句(i=2)尚未执行。一个变量在被初始化之前,其值是不确定的。

image-20221212204334287

再次点击Next line,第19行被执行,可见变量列表中的i值变成了2。黄色箭头来到了第20行,这是for循环的条件判断表达式。

image-20221212204642653

继续点击Next line,for循环在进行了条件判断后,准备执行循环体,黄色箭头来到第23行。

第23行涉及一次函数调用,如果操作者希望观察isPrime( )函数内部的执行过程,应按Step into。如果操作者不关心isPrime( )内部的执行过程,只关心23行的总体执行结果,则按Next line。本次,我们按Step into。

image-20221212205026961

按了Step into后,我们进入到了isPrime( )函数内部,黄色箭头指向第6行。对于isPrime( )函数而言,当前其局部变量仅有1个,即函数的形式参数n。该参数的值应由调用者传递进来。在Watches列表中,我们看到该值为2。

image-20221212205158476

一直按Next line,直到isPrime()返回,由于2是质数,所以isPrime()函数返回真。

image-20221212205329632

第23行的条件判断不成立,再次按下Next line后,第24行被略过,黄色箭头来到第25行。

image-20221212205628885

再次按下Next line,黄色箭头来到第26行,Watches列表可见iFound变量由0变1。

image-20221212205828152

一直按Next line进行循环,直到i = 4且黄色箭头指向第23行。

image-20221212205934663

本次我们选择不进入isPrime()函数,再次按下Next line。由于i=4不是质数,因此第23行的条件成立,黄色箭头跳转到第24行的continue语句。

根据continue的语义,其将略过本轮循环的剩余语句(第25 ~ 26行),直接尝试下一轮循环。

image-20221212210043211

按下Next line, 可以看到,continue直接略过了第25 ~ 26行的剩余循环代码,直接跳到了for循环的更新表达式,如图中红色箭头所示。严格地讲,此处的黄色箭头的定位并不准确,更准确的定位应该是第21行。

image-20221212210238266

再次按Next line,可以看到黄色箭头来到第20行。在该次动作中,事实上被执行的代码行是第21行,即for循环中的更新语句。在Watches列表中可以看到i由4变5。

根据for循环的语义,接下来第20行将进行for循环测试,如果测试通过,将继续循环,否则结束循环。

5.观察输出

image-20221212210743364

在程序调试过程中,可以随时通过Alt+Tab组合键或者点击Windows任务栏图标切换至TERMINAL(终端),该终端中展示了程序当前执行阶段的全部输出。如果程序在执行过程中需要读取输入,也应在其中录入。

可以看到,2,3两个质数已被找出,4是合数被略过,正准备考察整数5。

6. 去除断点

如果调试的目的已达成,准备放弃调试,可以再次单击红色断点将其取消,如下图(第18行左侧红框处):

image-20221212210821127

此时,可以选择Debug/Continue,在没有断点的情况下将程序执行完;也可以选停止调试,强行中止程序的执行。

7. 继续执行

取消断点后,我们选择了Debug/Continue。程序执行完成后,IDE中与调试相关的箭头,局部变量列表等均消失不见,恢复至常规编辑界面。