使用GDB调试程序的一般步骤:
第一步:编译程序(-g参数告诉编译器生成调试信息)
>gcc test.c -g
第二步:运行gdb
>gdb a.exe
第三步:查看源码,l列出居首的10行代码,字母l是list的缩写,注意:这里"(gdb)"是gdb的提示符,不是我们手工键入的。
(gdb) l
第四步:加断点(字母b是break的缩写) ,这里以在第四行加断点为例。
(gdb) b 4
第五步:执行(字母r是run的缩写),因为前面的b命令把断点设在了第4行,所以执行r命令后程序执行到这里停了下来。
(gdb) r
第六步:查看调用栈
(gdb) bt // bt是backtrace的缩写,表示回溯跟踪。#0表示当前栈帧,#1表示当前栈帧的上一个栈帧,以此类推。
(gdb) p a // p是print的缩写,查看当前栈帧中参数a的值。
(gdb) up // up命令选择上一个栈帧
(gdb) p a // 查看当前栈帧中参数a的值
其它gdb常用命令:
b function // b+函数名,将断点设置在函数开头。
s // step,执行一条语句,如果有函数调用,则进入函数内部。
n // next,执行一条语句,如果有函数调用,则把它作为一个整体。
实际上,我们可以写一个递归程序,来比较一下一般的调用(非自身)和自身的递归调用之间的区别,可以看到,在C语言中,调用自己和调用其他函数并没有任何本质的区别,都是建立新栈帧,传递参数并修改“当前代码行”(控制转移)。在函数体执行完毕后删除栈帧,处理返回值并修改“当前代码行”。
>size a.exe 命令可以查看可执行文件中各个段的大小。一个可执行文件由text(正文段,储存指令)、data(数据段,储存已初始化的全局变量)、bbs段(储存未初始化的全局变量)组成。 在运行期调用栈被创建,其所在的段为堆栈段,和其他段一样,有自己的大小,不能越界访问,否则就会出现段错误,也就是所谓的栈溢出,由于堆栈段大小固定,因此递归调用太多或是局部变量太大都可能导致栈溢出,因此我们常常把大数组定义成全局变量。
参考资料:《算法竞赛入门经典》——刘汝佳