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循环