Ubuntu12.04下gdb调试C语言简单程序详解

来源:互联网 发布:女生大数据 编辑:程序博客网 时间:2024/06/03 04:35

一 reverse

        第一个源程序是在上一篇gcc编译的博客里,是个出现的问题很easy的程序。源代码:源代码。因为问题不大,源代码也并不长,所以涉及到的指令都是常用的基础指令。

        reverse顾名思义就是反转字符串。但是运行之后是这样的:

        问题表现的超明显啦,不多说,放技能~

   

        1. 终端输入:

cd ~/Dwonloadsgdb reverse//也可以只输入gdb,然后输入 file reverse加载文件,即file +filename。

         ~/Downloads是存储reverse的目录,先进入这个目录。然后gdb filename进入调试模式,如图:

        2. 明显什么都看不到,所以要用 list 命令显示出我们想看到的源代码,默认list显示从文件中间行   作为显示的中间行   上下拓展共10行,因为我们主要调试的是reverse函数,所以直接显示函数,终端输入:

list reverse //list可以简写成l
        这样就会显示出reverse函数,即list function name 显示某个函数的源代码。但仅仅是10行,显示了才一半,所以我们要修改一下list显示的行数,终端输入:
set listsize 15//修改list一次性显示的代码数list reverse//也可以list 1 15 输出1-15行,即list line1 line2 

        即 set listsize number 设置list 一次性显示的代码的行数。可以把set 改成show 察看此时的list的显示行数。如图,效果刚刚好。


        3. 接下来是设置断点,这一步要在运行之前至少设置一个,否则运行起来就无法控制了。因为这个函数比较简单,所以只要在第9行循环处设置断点就好了。终端输入:

break 9 //直接break后面接行数。break reverse //在函数的中间点设置断点,不一定是你想要的点!//break 12 if i>1  如果i>1就在第12行停止,同下面的watch用法相同。即 break linenum if condition
        4. 终端输入:
run//开始运行程序// 也可以输入缩写 r
        提示输入字符串:

        输入ABCDEFG,回车进入运行,可以看到到了断点这里停下了,如下:

        其实在这里断点很不科学,应该在11行设置,但这里要介绍一下watch,所以就先在9行这里停一下。

        在终端输入:

watch i//或 watch i if i<0 

        watch +变量名,当i的值变化时会自动停止执行。 watch +变量名+if +条件语句 可以当条件触发时断开,由于循环是i>=0,所以每次当i变化时都会停。如果循环1000次,设置条件 watch i if i>997,可以让我们在任意想停下的循环次数停止而不用手动一次次的循环。       

        在终端输入:

info break//info locals 显示现在所在的函数内的变量值。
        可以输出所有断点的信息,如图:

        watch监视的是一个变量,只要变量发生变化不管在第几行都会停止。而break只有在运行到第9行才会停。

        在终端输入:

delete 1//或 d 1 ,只写delete的缩写d也可以。
        删除第一号断点,删除后在重新建立其他断点或监视点也不会是1号,而是一直向后排。

        终端输入:

continue//c 缩写,可以替换写/* --------------辨析--------------*Step 是单步执行,遇见函数会跳入函数内,c是直到断点才会停止。*Next 也是单步执行,但遇到函数也会一步执行完,不会进入函数。可以在后面加上整数来跳若干步。*/
        继续执行程序。此时i =1触发监视点,于是停了下来。可以看到有显示i的old和new值作为对比,但在本程序中监视c要比i好,所以终端输入新建i的监视点:

info break//如果很清楚要删除第几号断点可以不察看。delete 2// 可以用 clear N 删除第N行以上所有的断点。 watch cc//继续执行 
        可以看到因为c=*str+i执行后c变化了,所以又停下了:

        注意,函数本来是反转字符串,可以看到A和G已经成功调换,但现在c='H',证明错误已经出现了。终端输入:

info locals
        可以察看当前函数内的变量值:

        此时i=1,按照正常执行程序c='B'才对,而i=1只执行了 c=*str+i这一步,所以我们先着重看一下这一句,终端输入:

print *str p *str+i//p是print的缩写,可替换。
        print+变量名 即输出变量对应的现在的值,如图:

        这里可以从两个方面分析,第一个是值,我们写c = *str+i 本意是让指针地址指向下一个字符,但现在变成了str的ASCii值加i,所以这里应该是*(str+i)。另一个方面是类型,我们知道*str是char*类型的,指向下一个字符也应该是字符类型,但上下对比可以发现*str+i 至少不是char*,终端输入:

ptype *str+i  //显示表达式或变量运算后的值的类型。//输出  type = int ptype *(str+i)//输出  type = char
        所以至少有一个问题出现在这里,那么还有没有错误?我们先把c的值手动改回来,终端输入:

set var c='B'continue
        set var 后面接变量名=新值可以手动改变量的值。然后继续运行。

        可以看到第二次交换完成进行第三次交换时c的值再次变化,由于未更改源代码,所以c的值依旧是错的,但可以看到在New value下面的一行有str的结果,可以看到由于我们改变了c的值,所以str的倒数第二位是正确的 ‘B’ ,但正数第二位却成了‘L’,所以还有错误。可以看到第12行有和第11行同样的问题,用同样的方法测试其属性:

ptype *(str+i)//type = charptype *str +len-i-1//type = int
        所以这个语句也要改成*(str+i) = *(str+len-i-1)。第十三行由于c对str的末尾赋值并没有出现越界等错误,所以是没有错误的。到此这个程序就调试完成了。终端输入:

finish/*--------------------辨析-------------*return 也可以结束一个函数,但是并不会执行接下来的步骤!后面可以加 可作为函数返回值的值 作为返回值。*/
        就会运行直到跳出函数reverse。

        按c继续运行完毕,执行完程序后终端输入:

quit//q 缩写,某些特殊时候无法正常退出时,可以在后面加上!来强制退出。
        会结束gdb模式。回归普通终端模式。改掉源代码中的错误,再次运行,发现结果是正确的。

       

二 gdb-tui模式

        普通模式终端下输入:

gdb -tui
        会调用gdb的tui模式,即文本用户界面(交互式窗口),

        按回车翻倒下一页,或者q+回车进入gdb Shell模式。

        终端输入:

file reverse
        成功加载文件后上部分的窗口会自动调用该源代码文件。

       

       使用tui模式好处是可以很好的观察源代码,不用list频繁控制,但需要注意几点:

                1. 此时按上下键控制的是显示源代码的窗口,命令行的上下键是Ctrl+n/p。

                2. 在输入命令时无法插入,如果某个地方输入错误需要删到该处进行修改。

                3. 如果不是一步式编译(见gcc编译详解),而是在源代码文件夹内有中间生成的汇编代码,那么上部分窗口是会默认显示汇编文件的代码而不是源代码。


三 补充常用命令:

                1. 输入回车可以重复上一个命令。

                2. 如果在一个函数中只是想退出循环体而不是函数体可以把语句执行到循环体首行(for语句位置),输入 until(u)就可以执行完循环体了。

                3. jump N 可以跳到第N行,是不执行中间行,即不会更新堆栈,所以尽量不要在不同函数之间跳,跳后会直接继续运行直到断点才会停止。

               4.对于数组型变量可以用 print *str@num来显示前num个元素,但不要越界。
               5. whatis+变量名 和ptype+变量名一样可以打印出变量的类型信息。

四 总结

        对于这个例子的调试过程,显得有些勉强,而且也比较简单,但基本调试用到的命令几乎都提到了,有的地方加了拓展对比,对于复杂源代码的调试,是需要时间和精力练习的,从简单的源代码问题的调试一点点适应gdb是一件不那么让人反感的事情。在论坛上有人问过gdb的优点,一开始我也不爽为什么要学这么“底层”到D炸天的东西,看完后大概有一点点心甘情愿的感觉,反正感觉好像很全能的赶脚,vim也一样,债多不压身,一样一样慢慢熟练 T_T 。gdb的优点

0 0