1.6. 小熊猫C++ 中的程序调试方法
1.6. 小熊猫C++ 中的程序调试方法
编译时可以检查出源代码中的语法错误,但是检查不出源代码中的逻辑错误。有时候程序虽然能够编译并运行,但是运行结果却不是预想的结果,那就是因为源代码中存在有逻辑错误。主要是逻辑边界值的调试确定。
要想清除程序中的逻辑错误,就必须学会一些调试方法,通过观察变量的数值变化,加上自己的逻辑思考,从而找出程序中的错误所在并修改清除之。
一种常见的办法是在程序中添加输出语句,输出运行过程中的变量的中间值。例如上面示例程序中,可以在循环中添加语句,输出变量 k 和 sum 在循环过程中的值(如下图所示)。
#include <stdio.h>
int main() {
int sum = 0;
for (int k = 0; k <= 100; k++) {
sum += k;
Printf("k = %d, sum = %d\n", k, sum);
}
printf("sum = %d\n", sum);
return 0;
}
再次编译并运行程序,输出了一系列数据。仔细查看输出的数据(需要用鼠标拖动窗口右侧的滚动条,以便看到完整的输出结果),可以发现,上面程序只是从 1 累加到 99,所以累加结果是4950 。
要解决这个程序中的错误,就需要把 for 循环中的“k < 100”改为 “k <= 100”。 重新编译并运行,就会发现,程序最终输出结果是 5050,这是从 1 累加到 100 的正确结果。
上面程序通过输出运行过程中的变量的中间值可以找出问题并排除错误,但是有些程序的错误可能比较隐藏比较深,这时就需要更复杂一点的方法,也就是使用集成开发环境所提供的调试工具。
小熊猫C++提供了强大的除错和调试工具。工具栏上与调试有关的按钮是“开始调试”、“单步跳过”、“单步进入”、“单步跳出”、“继续执行”、“停止执行”和“添加监视”(下图所示)。在调试过程中需要熟练使用这几个按钮。

1.开始调试(Debug)
我们以如下一个含有错误的程序为示例,讨论如何进行调试。
#include <stdio.h>
double average(int length, int array[]) {
int k, sum;
for (k = 0; k <= length + 1; k++)
sum += array[k];
return sum / k;
}
int main() {
int a[] = {87, 74, 68, 86, 78, 82, 90};
int len = sizeof(a) / sizeof(a[0]);
printf("Total: %d\n", 87 + 74 + 68 + 86 + 78 + 82 + 90);
double avrg = average(len, a);
printf("Length: %d, Average: %f\n", len, avrg);
return 0;
}
该程序中含有一个名为“average”的函数,用于求出长度为 length 的整型数组 array[] 的平均值。在主函数中提供了一个示例数组 a ,并调用 average 函数求平均值。
把这个程序保存到本机,编译并运行。手工简单计算可知,数组 a 中现有的数据的平均值大约是 80.7 ,但是程序输出结果不正确,说明程序中存在严重的错误,而且无法直接找出错误所在。
点击工具栏上的“
开始调试”按钮(快捷键 F5),或点击菜单“运行”下的“调试 F5”即可开始调试。
2. 调试过程中的操作
如下图所示,启动调试之后,工具栏上的“单步跳过”、“单步进入”、“单步跳出”、“继续执行”和“停止执行”都变亮可用了,左侧管理器面板会自动切换到“监视”窗格。底部信息面板也自动展开并显示到“调试”页。
在上图中我们可以看到,启动调试后,“调试”面板的右边部分显示了“局部变量”标签页,其中显示局部数组变量 a 、局部变量 len 和局部变量 avrg 的值,而且是一些奇怪的莫名其妙的数字。这是因为刚进入函数时,所有局部变量都只是被分配了存储空间,还没有被赋值。所以查询其值就得到一个毫无意义的数字。需要在程序中赋值之后,它们才具有我们所需的值。读者不要为此感到惊奇。
然后需要熟练使用工具栏上的“单步跳过”按钮(快捷键 F7)和“单步进入”按钮(快捷键 F8)。在简单的只含有一个 main 函数的程序中,这两个按钮的功能是相同的,没有区别。而在含有多个自定义函数的程序中,这两个功能有较大的区别:“单步跳过”是指把当前语句作为一步执行完毕,而“单步进入”是指如果当前语句中含有函数调用则追踪进入到函数中去执行。
如果调用函数是标准函数或你认为无误的函数,就用“单步跳过”执行(以免追踪进入到函数中),对于怀疑有问题的函数才用“单步进入”去追踪。如果一不小心进入了标准函数(例如 printf 函数或“cout << endl;”输出),则点击“单步跳出”以返回。
在启动了调试之后,读者可以使用“单步跳过”按钮和“单步进入”按钮,慢慢地在调试状态下执行程序中的语句,并随时观察下方“局部变量”标签页中所显示的值,并且不断地思考程序中的语句是否正确,然后进行相应操作。
对于上面程序,应该在第15句时使用“单步进入”,追踪到 average 函数中去。仔细观察可以发现,该函数中的局部变量 sum 没有赋初值!
这时请点击工具栏上的“停止”按钮(快捷键 F6 ),返回到编辑状态,把局部变量 sum 赋初值为 0(例如写成“int k, sum = 0 ;”,然后点击“编译”按钮重新进行编译,再点击“运行”按钮运行程序,或点击“调试”按钮启动调试。(以后在调试过程中可以随时点击“停止”按钮返回编辑状态,对源代码进行修改之后需要重新编译,再运行程序或启动调试)
运行程序会发现结果仍然不正确,所以需要重新启动调试。分析可知,这时需要检查 for 循环中的情况。检查时会逐渐注意到,虽然下方“局部变量”标签页中随时会显示 k 和 sum 的值,但是重要的是需要观察 array[k] 才行!然而底部的“局部变量”标签页中并没有显示这个数组元素的值。
这时需要使用“监视变量”的功能。
在默认情况下,如果用鼠标悬浮在当前运行的代码中的变量上方(例如上面程序中的 length ),则会自动出现一个小悬浮窗口,显示出该变量当前的值。
如果想更快速地看到变量的值,可以在编辑器中选中待查看的变量名(例如,选中“array[k]”),点击工具栏上的“
添加监视”按钮并确认,左边的“监视”窗格中就会显示该变量当前的值。
在上面程序中添加了查看变量 array[k] 之后,用“单步进入”逐句执行该程序,并随时观察变量 k 和数组元素 array[k] 的值,最后会观察到,当 k 的值为 8 和 9 时仍然进入了循环!这是一个数组下标越界错误(主函数中的数组 a 只有 8 个变量,下标为 0 - 7。数组长度值 len 传递给函数 average 的参数 length,循环变量 k 最大只能取值为 7)。应该把 “k <= length + 1”修改为 “k < length”,这样才能避免下标越界,求得正确的 sum 值。
在上面的调试过程中,每次都使用“下一步”按钮(F7)和“单步进入”按钮(F8)进行操作时比较慢。还可以在程序中设置断点,加快调试操作速度。
在事先对程序的运行出错现象进行思考之后,可以推断出程序出错的可能原因在于源代码中的哪些语句。无论是在编辑状态还是在调试过程中,可以用鼠标点击想要暂停执行的那一行左边的装订区位置中的行号(或者把光标移动到想要那一行并按 F4 键),该行在装订区的行号处就显示有一个带勾的红圈,表示该行已被设置为一个断点。再次操作则取消该行为断点。
可以直接在“sum += array[k];”那一行设置一个断点,也可以在自己怀疑有问题的程序片段前后设置多个断点,以便进行调试。
在程序中设置了断点之后,启动调试时会直接运行到第一个断点处(而不是第一条可执行语句)暂停。这时仍然可以用“单步跳过”按钮(F7)和“单步进入”按钮(F8)进行操作。如果认为程序中某一部分无误,则点击“继续执行”按钮,就会继续运行到下一个断点处暂停,无断点则运行到程序末尾结束。
对上面的示例程序继续调试,可以发现语句“return sum / k;”含有错误:整数相除得到整数,丢失了小数部分。一种修改办法是改成“return 1.0 * sum / k;”。
这样,通过启动 小熊猫C++ 中的调试功能,使用“下一步”(F7)和“单步进入”(F8)慢慢地执行程序,在此过程中随时查看变量的值,并在头脑中进行分析思考,从而判断程序中是否存在逻辑错误,从而修改程序、排除程序中的错误。
最后,总结一下小熊猫C++ 调试过程中经常用到的功能及其快捷键:
设置断点 F4 启动调试 F5 停止执行 F6 单步跳过 F7
单步进入 F8 编译 F9 运行 F10 编译运行 F11
熟记这些快捷键有利于加快操作速度。
元·刘因《村居杂诗》
芳芬皆可籍,缓步即吾车[jū];
乘兴三杯酒,随行一策书[xū]。
曦海吞孤客,云途接太虚。
不悲残照晚,独向曙星初。
浪卷千秋雪,舟横万古淤。
暮天同曦渡,烟霞话瀛居。
千里客远去,万山影重疏。
江河驰昼夜,波浪自沉浮。
——羲海客·一策书(湘岳阳万江波)。
蒲公《咏史》
良马非不骏,盐坂徒悲鸣。
美玉非不贵,抱璞为世轻。
高士卧隆中,畴乃知其名?
从容起南阳,谈笑魏吴惊。
男儿事蚕桑,后世有何称?
——羲海客·一策书(湘岳阳万江波)

浙公网安备 33010602011771号