gdb调试(2):断点

23 篇文章 0 订阅
22 篇文章 0 订阅

转发自:http://songjinshan.com/akabook/zh/gdb.html#id1

看以下程序:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <stdio.h>

int main(void)
{
        int sum = 0, i = 0;
        char input[5];

        while (1) {
                scanf("%s", input);
                for (i = 0; input[i] != '\0'; i++)
                        sum = sum*10 + input[i] - '0';
                printf("input=%d\n", sum);
        }
        return 0;
}

这个程序的作用是:首先从键盘读入一串数字存到字符数组 input 中,然后转换成整型存到 sum 中,然后打印出来,一直这样循环下去。 scanf("%s", input); 这个调用的功能是等待用户输入一个字符串并回车, scanf 把其中第一段非空白(非空格、Tab、换行)的字符串保存到 input 数组中,并自动在末尾添加 '\0' 。接下来的循环从左到右扫描字符串并把每个数字累加到结果中,例如输入是 "2345" ,则循环累加的过程是(((0×10+2)×10+3)×10+4)×10+5=2345。注意字符型的 '2' 要减去 '0' 的ASCII码才能转换成整数值2。下面编译运行程序看看有什么问题:

$ gcc main.c -g -o main
$ ./main
123
input=123
234
input=123234
^C(按Ctrl-C退出程序)
$

又是这种现象,第一次是对的,第二次就不对。可是这个程序我们并没有忘了赋初值,不仅 sum 赋了初值,连不必赋初值的 i 都赋了初值。读者先试试只看代码能不能看出错误原因。下面来调试:

$ gdb main
...
(gdb) start
Temporary breakpoint 1 at 0x804843d: file main.c, line 4.
Starting program: /home/akaedu/main

Temporary breakpoint 1, main () at main.c:4
4    {
(gdb) n
5            int sum = 0, i = 0;

有了上一次的经验, sum 被列为重点怀疑对象,我们可以用 display 命令使得每次停下来的时候都显示当前 sum 的值,然后继续往下走:

(gdb) display sum
1: sum = 1466933
(gdb) n
9                    scanf("%s", input);
1: sum = 0
(gdb)
123
10                   for (i = 0; input[i] != '\0'; i++)
1: sum = 0

undisplay 命令可以取消跟踪显示,变量 sum 的编号是1,可以用 undisplay 1 命令取消它的跟踪显示。这个循环应该没有问题,因为上面第一次输入时打印的结果是正确的。如果不想一步一步走这个循环,可以用 break 命令(简写为 b )在第9行设一个断点(Breakpoint):

(gdb) l
5            int sum = 0, i = 0;
6            char input[5];
7
8            while (1) {
9                    scanf("%s", input);
10                   for (i = 0; input[i] != '\0'; i++)
11                           sum = sum*10 + input[i] - '0';
12                   printf("input=%d\n", sum);
13           }
14           return 0;
(gdb) b 9
Breakpoint 2 at 0x8048459: file main.c, line 9.

break 命令的参数也可以是函数名,表示在某个函数开头设断点。现在用 continue 命令(简写为 c )连续运行而非单步运行,程序到达断点会自动停下来,这样就可以停在下一次循环的开头:

(gdb) c
Continuing.
input=123

Breakpoint 2, main () at main.c:9
9                    scanf("%s", input);
1: sum = 123

然后输入新的字符串准备转换:

(gdb) n
234
10                   for (i = 0; input[i] != '\0'; i++)
1: sum = 123

问题暴露出来了,新的转换应该再次从0开始累加,而 sum 现在已经是123了,原因在于新的循环没有把 sum 归零。可见断点有助于快速跳过没有问题的代码,然后在有问题的代码上慢慢走慢慢分析,“断点加单步”是使用调试器的基本方法。至于应该在哪里设置断点,怎么知道哪些代码可以跳过而哪些代码要慢慢走,也要通过对错误现象的分析和假设来确定,以前我们用 printf 打印中间结果时也要分析应该在哪里插入 printf ,打印哪些中间结果,调试的基本思路是一样的。一次调试可以设置多个断点,用 info 命令可以查看已经设置的断点:

(gdb) b 12
Breakpoint 3 at 0x80484b2: file main.c, line 12.
(gdb) i breakpoints
Num     Type           Disp Enb Address    What
2       breakpoint     keep y   0x08048459 in main at main.c:9
        breakpoint already hit 1 time
3       breakpoint     keep y   0x080484b2 in main at main.c:12

每个断点都有一个编号,可以用编号指定删除某个断点:

(gdb) delete breakpoints 2
(gdb) i breakpoints
Num     Type           Disp Enb Address    What
3       breakpoint     keep y   0x080484b2 in main at main.c:12

有时候一个断点暂时不用可以禁用掉而不必删除,这样以后想用的时候可以直接启用,而不必重新从代码里找应该在哪一行设断点:

(gdb) disable breakpoints 3
(gdb) i breakpoints
Num     Type           Disp Enb Address    What
3       breakpoint     keep n   0x080484b2 in main at main.c:12
(gdb) enable 3
(gdb) i breakpoints
Num     Type           Disp Enb Address    What
3       breakpoint     keep y   0x080484b2 in main at main.c:12
(gdb) delete  breakpoints
Delete all breakpoints? (y or n) y
(gdb) i breakpoints
No breakpoints or watchpoints.

gdb 的断点功能非常灵活,还可以设置断点在满足某个条件时才激活,例如我们仍然在循环开头设置断点,但是仅当 sum 不等于0时才中断,然后用 run 命令(简写为 r )重新从程序开头连续运行:

(gdb) break 9 if sum != 0
Breakpoint 4 at 0x8048459: file main.c, line 9.
(gdb) i breakpoints
Num     Type           Disp Enb Address    What
4       breakpoint     keep y   0x08048459 in main at main.c:9
        stop only if sum != 0
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/akaedu/main
123
input=123

Breakpoint 4, main () at main.c:9
9                    scanf("%s", input);
1: sum = 123

结果是第一次执行 scanf 之前没有中断,第二次却中断了。总结一下本节用到的 gdb 命令:

gdb基本命令2
命令描述
break(或b) 行号在某一行设置断点
break 函数名在某个函数开头设置断点
break ... if ...设置条件断点
continue(或c)从当前位置开始连续运行程序
delete breakpoints 断点号删除断点
display 变量名跟踪查看某个变量,每次停下来都显示它的值
disable breakpoints 断点号禁用断点
enable 断点号启用断点
info(或i) breakpoints查看当前设置了哪些断点
run(或r)从头开始连续运行程序
undisplay 跟踪显示号取消跟踪显示

习题

  1. 看下面的程序:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    #include <stdio.h>
    
    int main(void)
    {
            int i;
            char str[6] = "hello";
            char reverse_str[6] = "";
    
            printf("%s\n", str);
            for (i = 0; i < 5; i++)
                    reverse_str[5-i] = str[i];
            printf("%s\n", reverse_str);
            return 0;
    }
    

    首先用字符串 "hello" 初始化一个字符数组 str (算上 '\0' 共6个字符)。然后用空字符串 "" 初始化一个同样长的字符数组 reverse_str,相当于所有元素用 '\0' 初始化。然后打印 str ,把 str 倒序存入 reverse_str ,再打印 reverse_str 。然而结果并不正确:

    $ ./main
    hello

    我们本来希望 reverse_str 打印出来是 olleh ,结果打出来一个空行。重点怀疑对象肯定是循环,那么简单验算一下, i=0 时, reverse_str[5]=str[0] ,也就是 'h' , i=1 时, reverse_str[4]=str[1] ,也就是 'e' ,依此类推,i=0,1,2,3,4,共5次循环,正好把h,e,l,l,o五个字母给倒过来了,哪里不对了?请用 gdb 跟踪循环,找出错误原因并改正。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值