gdb调试程序3


  • gdb 基本命令3

命令描述
watch设置观察点
info ( 或 i ) watchpoints查看当前设置了哪些观察点
x从某个位置开始打印存储单元的内容,全部当成字节来看,而不区分哪个字节属于哪个变量

接着上次的内容:

修改后的代码

  1 #include <stdio.h>
  2 
  3 int main(void)
  4 {
  5 
  6     int sum = 0, i = 0;
  7     char input[5];
  8 
  9     while(1){
 10         sum = 0;
 11         scanf("%s",input);
 12         for (i = 0;input[i] != '\0';i++)
 13             sum = sum*10 + input[i] - '0';
 14         printf("input=%d\n",sum);
 15     }
 16     return 0;
 17 }

使用scanf函数是非常凶险的,即使修正了这个Bug也还存在很多问题。如果输入的字符串超长了会怎么样?我们知道,数组访问越界是不会检查的,所以scanf会写出界。

# ./main
123
input=123
67
input=67
12345
input=123407

下面用gdb来调试看看这个结果是怎么出来的

  • x命令 按字节来打印某个位置的存储单元的内容
# gdb main
...
...
(gdb) start
Temporary breakpoint 1 at 0x40059e: file main.c, line 4.
Starting program: /home/liyongfeng/code/gdb_Demo/Demo2/main 

Temporary breakpoint 1, main () at main.c:4
4   {
(gdb) n
6       int sum = 0, i = 0;
(gdb) 
10          sum = 0;
(gdb) 
11          scanf("%s",input);
(gdb) 
12345
12          for (i = 0;input[i] != '\0';i++)
(gdb) p input
$1 = "12345"
(gdb) x/7bx input
0x7fffffffde50: 0x31    0x32    0x33    0x34    0x35    0x00    0x00

x 命令打印指定存储单元里保存的内容,后缀7bx是打印格式,7表示打印7组,b表示每个字节一组,x表示按十六进制格式打印,x/7bx这条命令从input数组的第一个字节开始连续打印7个字节。前5个字节是input数组的存储单元,打印的是十六进制的ASCII码的‘1’到‘5’,第6个字节是写出界的’\0’.


根据运行结果,前4个字符转成数字都没错,第5个错了,也就是说i从0到3的循环都没错,可以设置一个断点从i等于4开始单步调试:

(gdb) b 13 if i == 4
Breakpoint 2 at 0x4005e1: file main.c, line 13.
(gdb) c
Continuing.

Breakpoint 2, main () at main.c:13
13              sum = sum*10 + input[i] - '0';
(gdb) p sum
$2 = 1234

现在sum是1234没有错,根据运行结果是123407,我们知道,即将进行的下一步计算肯定要出错,算出来应该是12340,那就是说input[4]肯定不是‘5’了,下面的代码证明这个推理是不正确的:

(gdb) x/7bx input
0x7fffffffde50: 0x31    0x32    0x33    0x34    0x35    0x04    0x00

input[4]确实是0x35,再分析一下,可以发现,产生123407这个结果还有另外一种可能,就是在下一次循环中123450不是加上而是减去一个数,得到123407,因为循环结束的条件是input[i]!=’\0’,而本来应该是0x00的位置现在莫名其妙地变成了0x04,因此循环还没结束。继续单步调试:

(gdb) n
12          for (i = 0;input[i] != '\0';i++)
(gdb) p sum
$3 = 12345
(gdb) n
13              sum = sum*10 + input[i] - '0';
(gdb) x/7bx input
0x7fffffffde50: 0x31    0x32    0x33    0x34    0x35    0x05    0x00

进入下一次循环,原来的0x04又莫名其妙地变成了0x05,这是怎么回事?这个暂时解释不了,但123407这个结果可以解释了,是12345x10 + 0x05 - 0x30得到的,虽然多循环了一次,但下次一定会退出循环了,因为0x05的后面是‘\0’。


input[4]后面的那个字节到底是什么时候改变的?可以用观察点(Watchpoint)来跟踪。断点是当程序执行到某一代码行时中断,而观察点是当程序访问某个存储单元时中断,如果我们不知道某个存储单元是在哪里被改动的,这时候观察点尤其有用。

下面删除原来设的断点,从头执行程序,重复上次的输入,用watch命令设置观察点,跟踪input[4]后面的字节。

(gdb) delete breakpoints 
Delete all breakpoints? (y or n) y
(gdb) start
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Temporary breakpoint 3 at 0x40059e: file main.c, line 4.
Starting program: /home/liyongfeng/code/gdb_Demo/Demo2/main 

Temporary breakpoint 3, main () at main.c:4
4   {
(gdb) n
6       int sum = 0, i = 0;
(gdb) 
10          sum = 0;
(gdb) 
11          scanf("%s",input);
(gdb) 
12345
12          for (i = 0;input[i] != '\0';i++)
(gdb) watch input[5]
Hardware watchpoint 4: input[5]
(gdb) i watchpoints 
Num     Type           Disp Enb Address            What
4       hw watchpoint  keep y                      input[5]
(gdb) c
Continuing.
Hardware watchpoint 4: input[5]

Old value = 0 '\000'
New value = 1 '\001'
0x4005ae in main () at main.c:12
12          for (i = 0;input[i] != '\0';i++)
(gdb) c
Continuing.
Hardware watchpoint 4: input[5]

Old value = 0 '\001'
New value = 1 '\002'
0x4005ae in main () at main.c:12
12          for (i = 0;input[i] != '\0';i++)
(gdb) c
Continuing.
Hardware watchpoint 4: input[5]

Old value = 0 '\003'
New value = 1 '\004'
0x4005ae in main () at main.c:12
12          for (i = 0;input[i] != '\0';i++)

这里可以看出,每次回到for循环开头的时候,改变了input[5]的值,而且每次加1,而循环变量i正是在每次回到循环开头之前加1,原来input[5]就是变量i的存储单元,换句话说,i的存储单元是紧跟在input数组后面的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值