GDB 单步调试

当程序中出现一些选择分支、跳转、循环结构时,如果你想知道程序的具体执行路径,可以使用GDB单步调试:在用户控制下,一步一步地“缓慢”执行,每运行一步,你都可以停下来,观察和打印一些变量的值。通过单步调试,可以让用户知道程序运行的每一个细节和路径,可以帮助我们更好地理解和调试程序。

接下来,还是以下面的程序为例,分别跟踪程序中if-else分支、循环分支的单步执行情况:

#include <stdio.h>

int main(void)
{
    int sum = 0;
    int i, num;

    printf("breakpoint 1\n");

    printf("input num: ");
    scanf("%d", &num);
    printf("breakpoint 2\n");

    if(num <= 0)
        printf("input invalid!\n");
    else
    {
        for(i = 1; i <= num; i++)
            sum += i;
        printf("sum = %d\n", sum);
    }

    printf("breakpoint 3\n");
    printf("goodbye!\n");
    return 0;
}

为了观察if-else分支的单步执行情况,我们需要在程序的第8行,即观察点 breakpoint 2的位置,设置一个断点,然后使用单步执行命令,一步一步单步执行就可以了:

root@ubuntu:/home/gdb# gdb a.out
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...
(gdb) b 12
Breakpoint 1 at 0x1200: file main.c, line 12.
(gdb) r
Starting program: /home/gdb/a.out 
breakpoint 1
input num: 3

Breakpoint 1, main () at main.c:12
12        printf("breakpoint 2\n");
(gdb) n
breakpoint 2
14        if(num <= 0)
(gdb) n
18            for(i = 1; i <= num; i++)
(gdb) 
19                sum += i;
(gdb) 
18            for(i = 1; i <= num; i++)
(gdb) 
19                sum += i;
(gdb) print sum
$1 = 1
(gdb) n
18            for(i = 1; i <= num; i++)
(gdb) 
19                sum += i;
(gdb) 
18            for(i = 1; i <= num; i++)
(gdb) 
20            printf("sum = %d\n", sum);
(gdb) 
sum = 6
23        printf("breakpoint 3\n");
(gdb) 
breakpoint 3
24        printf("goodbye!\n");
(gdb) 
goodbye!
25        return 0;
(gdb) 
26    }
(gdb) 
__libc_start_main (main=0x5555555551a9 <main>, argc=1, argv=0x7fffffffe0c8, 
    init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, 
    stack_end=0x7fffffffe0b8) at ../csu/libc-start.c:342
342    ../csu/libc-start.c: No such file or directory.
(gdb) 
[Inferior 1 (process 2797) exited normally]
(gdb) 
The program is not being run.
(gdb) q
root@ubuntu:/home/gdb#

在上面的调试信息中,我们可以看到:当程序运行到第12行的断点处暂停下来的时候,我们此时使用next(可简写为n)命令,就可以让程序一行一行地单步执行,不断敲击回车键,重复执行next命令,程序就可以一行一行的单步执行下去。

if-else分支的执行情况是:如果会运行if分支,进行条件判断,当条件为假时,则跳入到了else分支的for循环语句执行。

for循环的执行情况是:首先进行for循环判断,条件满足,执行for的循环体,执行完一次循环后,再次运行for循环条件判断。重复以上过程,直到for循环条件判断不满足,退出for循环。

继续往下单步,当main函数运行结束,使用return 0退出时,此时还会调用一些底层的库函数,清理a.out运行进程的各种资源和上下文环境,然后整个a.out运行进程才会关掉、退出。

小结

通过单步调试,我们可以更方便地了解程序运行的实际执行路径和细节。在单步过程中,每执行一步,你都可以暂停下来,观察一些变量和内存的值,看是否符合自己的预期值。通过单步调试,你会更加深刻理解C语言程序语句的具体执行过程,尤其是一些复杂的程序结构,比如if-else分支、for循环、switch-case等,他们是如何进行条件判断的,是如何循环的,这些运行细节都可以通过单步执行来进行观察。当然,如果你对这些细节还不够了解的话,可以先去看看C语言入门教程中的:for循环

《Linux三剑客》视频教程,从零开始快速掌握Linux开发常用的工具:Git、Makefile、vim、autotools、debug,免费赠送C语言视频教程,C语言项目实战:学生成绩管理系统。详情请点击淘宝链接:Linux三剑客