g++ main.cpp -o main
g++ main.cpp -g -Wall -Werror -o main
This 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.
如果你已经按照上面说的方法,在激活调试模式的状态下编译了你的程序,那你就已经为调试做好了准备,在下面的命令里,请把尖括号< >里的内容替换为与自己程序相对应的文本。
在终端(terminal)中输入
gdb
用上面提到的叫做main的程序做栗子,命令就是
gdb main
break
break后面输入断点在源代码中的行数。
正如你的猜测
run
当你把程序暂停下来的时候,你可以做很多重要的事情,但是最重要的无疑是检查你断点附近的内容。与此相关的命令是“list”,它会显示出与断点相邻的10行代码。
仅仅暂停和开始并不足以让我们很好的控制程序,GDB还提供了单步运行的命令“next”和“step”。它们俩有一点差别,那就是“next”严格保证控制点在当前范围,而“step”会跟随执行进入到函数内部。
请认真看下面的栗子
value=display();
readinput();
如果使用next命令,执行完第一行后控制点跳到第二行readinput(),你可以检验value的值看看display()是否正常工作。
如果使用step命令,你会直接跟踪display()的行为,控制点跳到display()函数内的第一行
当你想定位非正常工作的代码在程序中的哪个部分时,检查局部变量的值往往是一个很有效的方法。可以用以下命令检查变量
print
你也可以用以下命令修改变量的值
set =
你可以看看修改变量值以后问题是否解决了,或者是否使程序进入了另外的分支,以此来判断bug是否来源于错误的变量值。
设置监视点就像是让调试软件对选定变量的改变进行实况报道,每当变量值改变,程序暂停并提供改变的详细信息
这个命令可以设置一个简单的监视点:
watch
下面的栗子是当变量改变时GDB的输出:
Continuing.
Hardware watchpoint 2: variable
Old value = 0
New value = 1
0x08048754 at main.cpp:31
31 variable=isalpha(ch)
提示:你可以只在变量的作用域里为它设置监视点,因此,想要监视另一个函数或者内部语句块的变量是,现在那个范围设一个断点,当程序在那暂停时,设置监视点。
如果想要在你的程序暂停时退出程序,使用“kill”命令,如果想退出GDB,使用“quit”命令
下面的代码功能是计算一个数的阶乘,不过很遗憾它是错的,我们的目标是调试它从而找到错误的原因。
#include
using namespace std;
long factorial(int n);
int main()
{
int n(0);
cin>>n;
long val=factorial(n);
cout<
现在请认真的看清楚每个命令和输出,尤其是监视点,这些内容都非常基础:
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. ...
8. Reading symbols from /home/manasij7479/Documents/main...done.
9. (gdb) break 11
10. Breakpoint 1 at 0x80485f9: file main.cpp, line 11.
11. (gdb) run
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. (gdb) step
18. factorial (n=3) at main.cpp:19
19. 19 long result(1);
20. (gdb) list
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. (gdb) watch n
32. Hardware watchpoint 2: n
33. (gdb) watch result
34. Hardware watchpoint 3: result
35. (gdb) continue
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
49. 0x08048654 in factorial (n=2) at main.cpp:20
50. 20 while(n--)
51. (gdb)
52. Continuing.
53. Hardware watchpoint 3: result
54.
55. Old value = 1
56. New value = 2
现在result变成了2(因为将result之前的值和n相乘),我们发现了第一个bug!result应该用3*2*1来求值,但是这里却从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)
65. 0x08048654 in factorial (n=1) at main.cpp:20
66. 20 while(n--)
67. (gdb)
68. Continuing.
69. Hardware watchpoint 2: n
70.
71. Old value = 1
72. New value = 0
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之前中止。
81. factorial (n=0) at main.cpp:20
82. 20 while(n--)
83. (gdb)
84. Continuing.
85. Hardware watchpoint 2: n
86.
87. Old value = 0
88. New value = -1
89. 0x08048654 in factorial (n=-1) at main.cpp:20
90. 20 while(n--)
91. (gdb)
92. Continuing.
现在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.
这就是被监视的变量离开作用域以后会发生的事情。
99. 0x08048605 in main () at main.cpp:11
100. 11 long val=factorial(n);
101. (gdb) print val
102. $1 = 1293357044
print val输出了一个垃圾值,因为gdb指向的是函数执行前的某一行。
103. (gdb) next
104. 12 cout<
下面是程序修正:
while(n>0) //doesn't let n reach 0
{
result*=n;
n--; //decrements only after the evaluation
}
现在你可以自己去试试GDB了,为了简化教程,一些重要的内容并没有提及,例如段错误和一些指针错误或是使用Valgrind来找到内存泄漏。
请记住GDB提供了一个优秀的帮助系统,在(gdb)模式下输入help,会出现帮助选项。如果想要了解特定命令,可以用命令:
help
另一个重要的点是快捷键的使用(例如q代表quit),GDB在没有歧义的情况下语序命令快捷键。
学习了GDB之后,下次你的程序发疯的时候你就不用恐慌了,现在你的武器库里有一个屌屌的武器了。