带栗子的GDB教程

原文链接:http://www.cprogramming.com/gdb.html作者:Manasij Mukherjee

一个好的调试软件是一个程序猿的工具箱里最重要的工具之一,在UNIX或Linux系统中,GDB(GUN debugger)是一个雄壮而流行的调试工具,它让你可以对在GDB下运行的程序做任何你爱做的事情。

我应该读这篇文章吗?

突击学习:用gcc编译程序

Gcc是Linux或其他所有*nix系统自带的编译器,它也有Window的接口,不过在Windows上还是用Visual Studio的debugger比较简单。

假设你的程序文件名叫main.cpp,你可以用以下的命令来编译它:

g++ main.cpp -o main

当这条命令执行完毕并生成了一个叫main的文件(程序无错误才会生成),你还需要在这个命令里加一个 -g, 告诉编译器你以后可能会调试这个程序。

g++ main.cpp -g -Wall -Werror -o main

如果这看起来很吓人,别担心,你会习惯的!(如果你有很多个文件,你可以用make或SCons来组建(build)他们)

好像混进了什么奇怪的东西,如果你好奇-Wall和-Werror的意思,请读-WallThis enables all the warnings about constructions that some users consider questionable, and that are easy to avoid (or modify to prevent the warning), even in conjunction with macros. This also enables some language-specific warnings described in C++ Dialect Options and Objective-C and Objective-C++ Dialect Options.

-Werror

Make all warnings into errors.

GDB基础

如果你已经按照上面说的方法,在激活调试模式的状态下编译了你的程序,那你就已经为调试做好了准备,在下面的命令里,请把尖括号< >里的内容替换为与自己程序相对应的文本。

开启GDB

在终端(terminal)中输入

gdb <executable name>

用上面提到的叫做main的程序做栗子,命令就是

gdb main

设置断点

为了可以检查程序,可能你会想让你的程序在某点停下来,你想要程序暂停的那一行叫做断点(我吻过你的脸,你双手曾在我的双肩~),

break <source code line number>

break后面输入断点在源代码中的行数。

运行程序

正如你的猜测

run

检查代码

当你把程序暂停下来的时候,你可以做很多重要的事情,但是最重要的无疑是检查你断点附近的内容。与此相关的命令是“list”,它会显示出与断点相邻的10行代码。

Next和Step

仅仅暂停和开始并不足以让我们很好的控制程序,GDB还提供了单步运行的命令“next”和“step”。它们俩有一点差别,那就是“next”严格保证控制点在当前范围,而“step”会跟随执行进入到函数内部。

请认真看下面的栗子

value=display();
readinput();

如果使用next命令,执行完第一行后控制点跳到第二行readinput(),你可以检验value的值看看display()是否正常工作。

如果使用step命令,你会直接跟踪display()的行为,控制点跳到display()函数内的第一行

检查变量

当你想定位非正常工作的代码在程序中的哪个部分时,检查局部变量的值往往是一个很有效的方法。可以用以下命令检查变量

print <var name to print>

你也可以用以下命令修改变量的值

set <var> = <value>

你可以看看修改变量值以后问题是否解决了,或者是否使程序进入了另外的分支,以此来判断bug是否来源于错误的变量值。

设置监视点

设置监视点就像是让调试软件对选定变量的改变进行实况报道,每当变量值改变,程序暂停并提供改变的详细信息

这个命令可以设置一个简单的监视点:

watch <var>

下面的栗子是当变量改变时GDB的输出:

Continuing.
Hardware watchpoint 2: variable Old value = 0
New value = 10x08048754 at main.cpp:31
31 variable=isalpha(ch)

提示:你可以只在变量的作用域里为它设置监视点,因此,想要监视另一个函数或者内部语句块的变量是,现在那个范围设一个断点,当程序在那暂停时,设置监视点。

退出

如果想要在你的程序暂停时退出程序,使用“kill”命令,如果想退出GDB,使用“quit”命令

一个栗子

下面的代码功能是计算一个数的阶乘,不过很遗憾它是错的,我们的目标是调试它从而找到错误的原因。

#include<iostream>
using namespace std;
long factorial(int n);
int main() {
int n(0);
cin>>n;
long val=factorial(n);
cout<<val;
cin.get();
return 0;
}
long factorial(int n) {
long result(1);
while(n--) {
result*=n;
}
return result;
}

开始调试

现在请认真的看清楚每个命令和输出,尤其是监视点,这些内容都非常基础:

  • 在函数调用的那一行设置断点
  • step进入函数
  • 为计算结果和输入数字设置监视点
  • 最后,从监视点中分析结果并找到错误
      1.$ g++ main.cpp -g -Wall -o main
2.$ gdb main
3.GNU gdb (GDB) Fedora (7.3-41.fc15)
4.Copyright (C) 2011 Free Software Foundation, Inc.
5.This GDB was configured as "i686-redhat-linux-gnu".
6.For bug reporting instructions, please see:
7.<http://www.gnu.org/software/gdb/bugs/>...
8.Reading symbols from /home/manasij7479/Documents/main...done.
9.<strong>(gdb) break 11</strong>
10.Breakpoint 1 at 0x80485f9: file main.cpp, line 11.
11. <strong>(gdb) run</strong>
12. Starting program: /home/manasij7479/Documents/main
13. 3
14.
15. Breakpoint 1, main () at main.cpp:11
16. 11 long val=factorial(n);
17. <strong>(gdb) step</strong>
18. factorial (n=3) at main.cpp:1919.
19 long result(1);
20. <strong>(gdb) list</strong>
21. 14 return 0;
22. 15 }
23. 16
24. 17 long factorial(int n)
25. 18 {
26. 19 long result(1);
27. 20 while(n--)
28. 21 {
29. 22 result*=n;
30. 23 }
31. <strong>(gdb) watch n</strong>
32. Hardware watchpoint 2: n
33. <strong>(gdb) watch result</strong>
34. Hardware watchpoint 3: result
35. <strong>(gdb) continue</strong>
36. Continuing.
37. Hardware watchpoint 3: result
38.
39. Old value = 0
40. New value = 1

注意到result开始为0然后初始化为1.

41.     factorial (n=3) at main.cpp:20
42. 20 while(n--)43. (gdb)

注意没有输入新命令,敲回车GDB会再次执行上一条命令

44.     Continuing.
45. Hardware watchpoint 2: n
46.
47. Old value = 3
48. New value = 2

注意到n立即从3变成了2

现在result变成了2(因为将result之前的值和n相乘),我们发现了第一个bug!result应该用321来求值,但是这里却从2开始乘。我们应该稍微修改一下循环来修正它,不过修改之前可以先看看计算的其他部分是否正确。

57.     factorial (n=2) at main.cpp:20
58. 20 while(n--)
59. (gdb)
60. Continuing.
61. Hardware watchpoint 2: n
62.
63. Old value = 2
64. New value = 1

n从2变成了1,result没有改变(因为n=1)

n从2变成了0

73.     0x08048654 in factorial (n=0) at main.cpp:20
74. 20 while(n--)
75. (gdb)
76. Continuing.
77. Hardware watchpoint 3: result
78.
79. Old value = 2
80. New value = 0

现在result也变成了0,因为它跟n现在的值相乘了。另一个bug!循环应该在n变成0之前中止。

现在n=-1所以循环不再运行,因为n--不满足循环条件,函数返回了result的当前值0,让我们看看函数退出时会发生什么。

93.
94. Watchpoint 2 deleted because the program has left the block in
95. which its expression is valid.
96.
97. Watchpoint 3 deleted because the program has left the block in
98. which its expression is valid.

这就是被监视的变量离开作用域以后会发生的事情。

print val输出了一个垃圾值,因为gdb指向的是函数执行前的某一行。

103.    (gdb) next
104. 12 cout<<val;
105. (gdb) continue
106. Continuing.
107. 0[Inferior 1 (process 2499) exited normally]
108. (gdb) quit

下面是程序修正:

while(n>0) //doesn't let n reach 0
{
result*=n;
n--; //decrements only after the evaluation
}

GDB 总结

现在你可以自己去试试GDB了,为了简化教程,一些重要的内容并没有提及,例如段错误和一些指针错误或是使用Valgrind来找到内存泄漏。

请记住GDB提供了一个优秀的帮助系统,在(gdb)模式下输入help,会出现帮助选项。如果想要了解特定命令,可以用命令:

help <command>

另一个重要的点是快捷键的使用(例如q代表quit),GDB在没有歧义的情况下语序命令快捷键。

学习了GDB之后,下次你的程序发疯的时候你就不用恐慌了,现在你的武器库里有一个屌屌的武器了。

【转】带栗子的GDB教程的更多相关文章

  1. XP系统电脑带安卓手机上网教程(无需adhoc补丁)

    XP系统电脑带安卓手机上网教程(无需adhoc补丁) WIN7系统可以虚拟wifi热点,安卓手机连上这个热点就能上网.XP系统虚拟出来的wifi热点是adhoc形式的,原生的安卓系统并不支持adhoc ...

  2. GDB教程

    GDB是一个由GNU开源组织发布的.UNIX/LINUX操作系统下的.基于命令行的.功能强大的程序调试工具. GDB中的命令固然很多,但我们只需掌握其中十个左右的命令,就大致可以完成日常的基本的程序调 ...

  3. 系统自带vim命令学习教程

    [环境] [干货分享] vim或者vi命令在很多linux环境中自带一款学习教程,其教程说明语言还是随系统变化. 输入vimtutor这个命令会打开一款学习神器. 打开之后显示如下,vimtutor一 ...

  4. 运行caffe自带的mnist实例教程

    运行caffe自带的mnist实例教程 本文结合几篇博文总结下来的,附上其中一篇原博文链接以供参考:http://blog.sina.com.cn/s/blog_168effc7e0102xjr1.h ...

  5. HttpWatch汉化版带详细的使用教程下载

    http://www.wocaoseo.com/thread-303-1-1.html HttpWatch是强大的网页数据分析工具.集成在Internet Explorer工具栏.包括网页摘要.Coo ...

  6. unity自带寻路Navmesh入门教程(一)

    说明:从今天开始,我阿赵打算写一些简单的教程,方便自己日后回顾,或者方便刚入门的朋友学习.水平有限请勿见怪.不过请尊重码字截图录屏的劳动,如需转载请先告诉我.谢谢! unity自从3.5版本之后,增加 ...

  7. unity3D ——自带寻路Navmesh入门教程(一)(转)

    转自:http://liweizhaolili.blog.163.com/blog/static/16230744201271161310135/ 说明:从今天开始,我阿赵打算写一些简单的教程,方便自 ...

  8. 【转】unity自带寻路Navmesh入门教程(一)

    http://liweizhaolili.blog.163.com/blog/static/16230744201271161310135/ 说明:从今天开始,我阿赵打算写一些简单的教程,方便自己日后 ...

  9. unity自带寻路Navmesh入门教程(三)

    继续介绍NavMesh寻路的功能,接下来阿赵打算讲一下以下两个例子,先看看完成的效果:   第一个例子对于喜欢DOTA的朋友应该很熟悉了,就是不同小队分不同路线进攻的寻路,红绿蓝三个队伍分别根据三条路 ...

随机推荐

  1. CentOS7 忘记Root密码解决方法

    1- 在启动grub菜单,选择编辑选项启动 ​ 2 - 按键盘e键,来进入编辑界面 ​ 3 - 找到Linux 16的那一行,将ro改为rw init=/sysroot/bin/sh ​ 4 - 现在 ...

  2. 『003』Shell命令

    『001』索引-Linux Shell Command shell命令 <01>[线上查询及帮助][001]-[001] [001]- 点我快速打开文章[man][help][已改版] & ...

  3. 苏州市java岗位的薪资状况(2)

    上一篇已经统计出了起薪最高的top 10: 接着玩,把top 10 中所有职位的详细信息爬取下来.某一职位的详情是这样: 我们需要把工作经验.学历.职能.关键字爬取下来. from urllib.re ...

  4. Fiddler安装

    1.在网上搜一下fiddler的安装包,下载.下载完成能看到一个exe文件. 2.点击文件安装,同意,选择一个目录进行安装即可. 3.安装完成,打开应用是一个这样的界面. 需要安装包的下面评论.... ...

  5. 201871010116-祁英红《面向对象程序设计(java)》第十三周学习总结

    博文正文开头格式:(2分) 项目 内容 <面向对象程序设计(java)> https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://ww ...

  6. 201871010123-吴丽丽《面向对象程序设计(Java)》第一周学习总结

                                                                            201871010123-吴丽丽<面向对象程序设计 ...

  7. win10系统使用小技巧【转】

    win10的很多小技巧又简单又实用,这里给大家整理了10个小技巧,一分钟学会,秒变win10高手,看不完的先收藏再看哦. 1.改美区 在设置中时间和语言中将区域和语言改为美国就可以瞬间切换Foreca ...

  8. Spring Cloud 教程

    Spring Cloud系列教程: Spring Boot + Spring Cloud 构建微服务系统(一):服务注册和发现(Consul) Spring Boot + Spring Cloud 构 ...

  9. CSP2019游记(翻车记)

    Preface 也许是人生中最重要的一场比赛了(再进不了冬令营我就没了) 结果不论怎样,想必也都是人生中的一次分水岭吧 从暑假开始到今天的一段时间,自己似乎终于找到了学OI的动力与乐趣.能认识到更多志 ...

  10. python进阶之垃圾回收

    内存使用: 程序在运行的过程需要开辟内存空间,比如创建一个对象需要一片内存空间,定义变量需要内存空间.有内存的开辟,势必也要有内存的释放,不然只进不出那不是貔貅了吗?即使有开辟有释放在短期内还是会有垃 ...