C 语言使用 gdb 调试

示例程序

c 复制代码
#include <stdio.h> 

int main(void) {
        char s[20] = {0};
        s[0] = 's';
        s[2] = 's';
        printf("%s\n", s);
        return 0;
}  

编译

bash 复制代码
$ gcc fun_printf.c -o fun_printf

运行

bash 复制代码
$ ./fun_printf 
s

查看示例程序,期望输出 s s,但实际只输出了一个s

查看 printf 函数的说明

bash 复制代码
$ man 3 printf

man 命令

章节 内容描述 示例
1 shell man ls, man printf
2 系统调用 man 2 unlink, man 2 open
3 C库函数 man 3 printf
4 特殊设备文件 man 4 null
5 文件格式与配置文件(如 /etc/passwd) man 5 passwd
6 游戏与屏保 man 6 intro
7 杂项 man 7 utf8
8 系统管理命令 man 8 fdisk

使用gdb 调试程序

重新编译程序 支持调试

bash 复制代码
$ gcc -g fun_printf.c -o fun_printf

启动 gdb 调试

bash 复制代码
$ gdb ./fun_printf 
GNU gdb (Ubuntu 15.1-1ubuntu1~24.04.1) 15.1
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./fun_printf...
(gdb) 

查看源码,设置断点

bash 复制代码
(gdb) l
1	#include <stdio.h>
2	
3	int main(void) {
4		char s[20] = {0};
5		s[0] = 's';
6		s[2] = 's';
7		printf("%s\n", s);
8		return 0;
9	}
(gdb) b 7
Breakpoint 1 at 0x11a3: file fun_printf.c, line 7.

开始运行程序

bash 复制代码
(gdb) run
Starting program: /home/scd/book/linux-c/ch02/fun_printf 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, main () at fun_printf.c:7
7		printf("%s\n", s);

进入函数

bash 复制代码
(gdb) s
0x00007ffff7c87bf8 in __GI__IO_puts (str=0x7fffffffd8f0 "s") at ./libio/ioputs.c:35
35	  size_t len = strlen (str);

发现 printf 函数变成了 puts

查看 puts 函数的源码

bash 复制代码
(gdb) l
30	
31	int
32	_IO_puts (const char *str)
33	{
34	  int result = EOF;
35	  size_t len = strlen (str);
36	  _IO_acquire_lock (stdout);
37	
38	  if ((_IO_vtable_offset (stdout) != 0
39	       || _IO_fwide (stdout, -1) == -1)
(gdb) 
40	      && _IO_sputn (stdout, str, len) == len
41	      && _IO_putc_unlocked ('\n', stdout) != EOF)
42	    result = MIN (INT_MAX, len + 1);
43	
44	  _IO_release_lock (stdout);
45	  return result;
46	}
47	
48	weak_alias (_IO_puts, puts)
49	libc_hidden_def (_IO_puts)
(gdb) 
End of the file was already reached, use "list ." to list the current location again

执行获取长度方法 strlen (str)

复制代码
(gdb) n
36	  _IO_acquire_lock (stdout);
(gdb) print len
$1 = 1

获取的长度只有1

跳出当前函数

bash 复制代码
(gdb) finish
Run till exit from #0  __GI__IO_puts (str=0x7fffffffd8f0 "s") at ./libio/ioputs.c:36
s
main () at fun_printf.c:8
8		return 0;
Value returned is $2 = 2

只打印出了一个s

但是用 gdb 的 print 的打印可以看到 2个 s

bash 复制代码
(gdb) print s
$3 = "s\000s", '\000' <repeats 16 times>

查看s的内存

bash 复制代码
(gdb) x/20bc s
0x7fffffffd8f0:	115 's'	0 '\000'	115 's'	0 '\000'	0 '\000'	0 '\000'	0 '\000'	0 '\000'
0x7fffffffd8f8:	0 '\000'	0 '\000'	0 '\000'	0 '\000'	0 '\000'	0 '\000'	0 '\000'	0 '\000'
0x7fffffffd900:	0 '\000'	0 '\000'	0 '\000'	0 '\000'

x/20bc s 是 GDB 中查看内存的命令,各部分含义如下:

  1. x → examine 的缩写,用于查看内存内容。
  2. 20 → 表示要显示 20 个 单位(后面会说明单位大小)。
  3. b → 单位是 字节 (byte),每次显示 1 个字节。
  4. c → 将每个字节以 字符 (character) 格式显示。
    从变量 s 的地址开始,以字符形式显示连续 20 个字节的内容

发现第2个字符是 \0, puts 函数在输出的时候遇到\0 就结束了,所以只输出了一个s。具体的还要看看 strlen (str) 的汇编代码

gdb 相关知识

  1. 编译准备
    在使用 GDB 之前,必须确保编译时加入了 -g 选项,以便在可执行文件中嵌入调试信息(如变量名、行号等)。建议同时关闭优化(-O0 a),以避免代码重排导致调试困难。
bash 复制代码
gcc -g -O0 -o program program.c

  1. 启动 GDB
    可以通过以下几种方式启动调试会话:
  • 加载可执行文件‌:gdb ./program
  • 加载 Core Dump 文件‌(用于分析崩溃):gdb ./program core
  • 附加到运行中的进程‌:gdb -p

  1. 核心调试命令
  • 设置断点 (Breakpoints)
    断点让程序在指定位置暂停执行。

break main 或 b main:在 main 函数入口设置断点。

break 10 或 b 10:在第 10 行设置断点。

break file.c:20:在指定文件的第 20 行设置断点。

break 20 if x > 10:设置条件断点,仅当 x > 10 时暂停。

info breakpoints 或 i b:查看当前所有断点信息。

delete 1:删除编号为 1 的断点。

  • 运行与控制执行

    run 或 r:启动程序,直到遇到第一个断点或程序结束。

    continue 或 c:从当前暂停处继续执行,直到下一个断点。

    next 或 n:单步执行下一行代码,‌不进入‌函数内部(Step Over)。

    step 或 s:单步执行下一行代码,‌进入‌函数内部(Step Into)。

    finish:执行完当前函数并返回到调用者处暂停。

    until 或 u:快速执行直到退出当前循环或到达指定行。

  • 查看数据与状态

    print variable 或 p variable:打印变量的值。

    p/x var:以十六进制显示。

    p *ptr:查看指针指向的内容。

    p arr@5:查看数组前 5 个元素。

    display variable:每次程序暂停时自动打印该变量的值。

    undisplay 1:取消编号为 1 的自动显示。

    backtrace 或 bt:查看函数调用栈,帮助定位崩溃来源。

    frame n 或 f n:切换到第 n 层栈帧,以便查看该层级函数的局部变量。

    info locals:查看当前栈帧的所有局部变量。

    info args:查看当前函数的参数。

    whatis var:查看变量的类型。

  • 监控变量变化 (Watchpoints)

    watch variable:当变量的值被修改时,程序自动暂停。

    rwatch variable:当变量被读取时暂停。

    awatch variable:当变量被读取或写入时暂停。

  • 其他实用命令

    list 或 l:显示源代码。l 10,20 显示第 10 到 20 行。

    set variable x = 10:在运行时修改变量 x 的值,用于测试不同逻辑分支。

    quit 或 q:退出 GDB。


gdb 初始化配置

配置库函数源码

bash 复制代码
$ echo "directory /home/scd/.c/glibc-2.39" >> ~/.gdbinit
相关推荐
caishenzhibiao3 小时前
期货先行者主图 同花顺期货通指标
java·c语言·c#
NoteStream6 小时前
【c语言基础】C语言常见概念
c语言·开发语言·编辑器
caishenzhibiao6 小时前
先行者副图 同花顺期货通指标
java·c语言·c#
回眸不遇6 小时前
将 Docker虚拟磁盘文件ext.vhdx迁移出C盘 ,更换到D盘
c语言·docker·容器
冻柠檬飞冰走茶6 小时前
PTA基础编程题目集 7-27 冒泡法排序(C语言实现)
c语言·开发语言·数据结构·算法
wuyk5556 小时前
77.嵌入式 C 语言中 enum 枚举的工程化实践:告别魔法数字,提升代码可维护性
c语言·开发语言·stm32·单片机·嵌入式硬件
lightqjx7 小时前
【Linux系统】基础 IO
linux·c语言·重定向·缓冲区·文件io·文件的系统调用
亿道电子Emdoor8 小时前
【Arm】MSP和PSP的区别简介
c语言·arm开发·单片机·mcu
2301_777998349 小时前
Linux线程同步与互斥(三):信号量与环形队列实现生产者消费者模型
linux·c语言·c++
c238569 小时前
《枚举算法 “翻译官”:用 enum class 给 “游游的礼盒” 算分》
c语言·c++·算法