天天看点

《C语言程序设计进阶教程》一2.7 在DDD(命令行调试程序)上检测调用栈

本文讲的是<b>C语言程序设计进阶教程一2.7 在DDD(命令行调试程序)上检测调用栈</b>,在编辑器中输入下面的程序,并把名字存为p1.c

《C语言程序设计进阶教程》一2.7 在DDD(命令行调试程序)上检测调用栈
《C语言程序设计进阶教程》一2.7 在DDD(命令行调试程序)上检测调用栈

不要担心还不能完全理解main函数中的argv,这将会在之后讨论。在Linux终端下使用下面的命令创建可执行文件:

《C语言程序设计进阶教程》一2.7 在DDD(命令行调试程序)上检测调用栈

这里使用gcc将C程序(p1.c)的源文件转化为计算机可以理解的可执行文件。添加-g启用调试,这样我们可以检验调用栈。添加-Wall和-Wshadow启用警告信息。影子变量将会在4.1节中进行讲解。警告信息有时是良性的,但是它们通常表明在代码中有深层的问题。总是启用警告信息并按照gcc的建议去做是一个好的习惯。输出文件(即可执行文件)的名字由-o所指定。在本例中,p1是此gcc命令的输出,因此是可执行文件(即程序)。它可以通过在终端中输入下列内容来运行:

《C语言程序设计进阶教程》一2.7 在DDD(命令行调试程序)上检测调用栈

输出应该和下面相同:

《C语言程序设计进阶教程》一2.7 在DDD(命令行调试程序)上检测调用栈

要看到调用栈,我们需要启动调试工具。在本例中,我们将使用DDD(命令行调试程序)。DDD是一个针对GDB调试工具的图形用户界面。启动DDD,进入菜单并点击:

File→Open Program→select p1→Open

这里,我们已经选择了可执行程序而不是.c文件。调试工具将自动地基于gcc留在可执行文件中的信息找到.c文件。当调试一个使用多个源文件的程序时这会很有用。

在底部窗口(gdb)提示之后,可在两个函数g1和g2中使用下面的指令设置断点:

《C语言程序设计进阶教程》一2.7 在DDD(命令行调试程序)上检测调用栈

指令b g1指示DDD在函数g1开始时设置一个断点。当程序到达g1的第一行,程序将停止,你会有机会检查程序的状态。指令b g2指示DDD在函数g2开始时类似地去设置一个断点。

通过在(gdb)提示时输入下面的指令来执行此程序:

《C语言程序设计进阶教程》一2.7 在DDD(命令行调试程序)上检测调用栈

程序将会启动,然后在函数g2的断点处停止。为什么程序在g2处停止而不是g1呢?因为main函数调用g2,所以g2在g1之前被执行。如果设置了多个断点,那么程序将按照它们被执行的顺序停止在断点处,而不是按照它们被设置的顺序。在本例中,虽然在g1中的断点是先设置的,但程序先执行g2。所以,程序将先停在g2中的断点处。

要继续这个程序,输入下面的指令:

《C语言程序设计进阶教程》一2.7 在DDD(命令行调试程序)上检测调用栈

程序将继续执行,之后停在下一个断点处,该断点位于函数g1中。调用栈可以通过调用栈回溯查看到。这可以用下面的指令完成:

《C语言程序设计进阶教程》一2.7 在DDD(命令行调试程序)上检测调用栈

这条指令的意思是“栈回溯”。你会在调试工具中看到什么?

《C语言程序设计进阶教程》一2.7 在DDD(命令行调试程序)上检测调用栈

a和b的值在顶部栈帧中显示。每一行的起始显示了调用栈的栈帧(0、1和2),与函数g1、g2和main函数相对应。可以使用f命令来看到不同的栈帧:例如,输入:

《C语言程序设计进阶教程》一2.7 在DDD(命令行调试程序)上检测调用栈

可以看到frame1,即函数g2的栈帧。a和b的值被再一次地显示。它们的值是多少呢?0x之后的数字在你的计算机上可能与此不同,这些就是地址。在g2的栈帧中,a和b的值与顶部栈帧中的值不同。图2.6~图2.9显示了一些DDD的截图。

《C语言程序设计进阶教程》一2.7 在DDD(命令行调试程序)上检测调用栈
《C语言程序设计进阶教程》一2.7 在DDD(命令行调试程序)上检测调用栈
《C语言程序设计进阶教程》一2.7 在DDD(命令行调试程序)上检测调用栈
《C语言程序设计进阶教程》一2.7 在DDD(命令行调试程序)上检测调用栈

原文标题:C语言程序设计进阶教程一2.7 在DDD(命令行调试程序)上检测调用栈

继续阅读