用GDB调试程序──查看运行时数据

参考自:http://hi.baidu.com/wg_wang/blog/item/dad263c2f5598630e5dd3ba6.html

 

     在使用GDB调试程序时,触发断点后,可以使用print命令(简写为p),或是同义命令inspect来查看当前程序的运行数据。print命令的格式是:    

    print <expr>
    print /<f> <expr>
<expr>
是表达式,是所调试程序的语言的表达式(GDB可以调试多种编程语言);<f>是输出的格式,例如如果要把表达式按16进制的格式输出,那么就是/x

一、表达式

    print和许多GDB的命令一样,可以接受一个表达式,GDB会根据当前的程序运行的数据来计算这个表达式,既然是表达式,那么就可以是当前程序运行中的const常量、变量、函数等内容。可惜的是GDB不能使用你在程序中所定义的宏(因为宏是在编译时就已经展开了的)。表达式的语法应该是当前所调试的语言的语法。   
    
在表达式中,有几种GDB所支持的操作符,它们可以用在任何一种语言中。    
    @        
是一个和数组有关的操作符,在后面会有更详细的说明。        
    ::         
指定一个在文件或是一个函数中的变量。        
    {<type>} <addr>        
表示一个指向内存地址<addr>的类型为type的一个对象。       

二、程序变量

    GDB中,你可以随时查看以下三种变量的值:
        1
、全局变量(所有文件可见的)
        2
、静态全局变量(当前文件可见的)
        3
、局部变量(当前Scope可见的)        
    
如果局部变量和全局变量发生冲突(也就是重名),一般情况下是局部变量会隐藏全局变量,也就是说,用print显示出的变量的值会是函数中的局部变量的值。如果此时想查看全局变量的值,可以使用“::”操作符:    
      file::variable
    function::variable
    
可以通过这种形式指定想查看的变量是哪个文件中的或是哪个函数中的。例如,查看文件f2.c中的全局变量x的值:
    (gdb) p 'f2.c'::x    
    
当然,“::”操作符会和C++中的发生冲突,GDB能自动识别“::” 是否C++的操作符,所以不必担心在调试C++程序时会出现异常。    
    
另外,需要注意的是,如果程序编译时开启了优化选项,那么在用GDB调试被优化过的程序时,可能会发生某些变量不能访问,或是取值错误码的情况。这是因为优化程序会删改你的程序,整理你程序的语句顺序,剔除一些无意义的变量等,所以在GDB调试这种程序时,运行时的指令和所编写指令就有不一样,也就会出现所想象不到的结果。对付这种情况时,可以在编译程序时关闭编译优化。一般来说,几乎所有的编译器都支持编译优化的开关,例如,GNUC/C++编译器GCC,你可以使用“-gstabs”选项来解决这个问题。关   

三、数组

    有时候,需要查看一段连续的内存空间的值。比如数组的一段,或是动态分配的数据的大小。可以使用GDB的“@”操作符,“@”的左边是第一个内存的地址的值,“@”的右边是想查看内存的长度。例如,程序中有这样的语句:     
        int *array = (int *) malloc (len * sizeof (int));        
    
于是,在GDB调试过程中,你可以以如下命令显示出这个动态数组的取值:

        p *array@len

    @的左边是数组的首地址的值,也就是变量array所指向的内容,右边则是数据的长度,其保存在变量len中,其输出结果,大约是下面这个样子的:    
        (gdb) p *array@len
        $1 = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40}

    如果是静态数组的话,可以直接用print数组名,就可以显示数组中所有数据的内容了。

四、输出格式

    一般来说,GDB会根据变量的类型输出变量的值。但也可以自定义GDB的输出的格式。例如,想输出一个整数的十六进制,或是二进制来查看这个整型变量的中的位的情况。可以使用GDB的数据显示格式:    
    x 
按十六进制格式显示变量。
    d 
按十进制格式显示变量。
    u 
按十六进制格式显示无符号整型。
    o 
按八进制格式显示变量。
    t 
按二进制格式显示变量。 
    a 
按十六进制格式显示变量。
    c 
按字符格式显示变量。
    f 
按浮点数格式显示变量。

        (gdb) p i
        $21 = 101           
        (gdb) p/a i
        $22 = 0x65        
        (gdb) p/c i
        $23 = 101 'e'        
        (gdb) p/f i
        $24 = 1.41531145e-43        
        (gdb) p/x i
        $25 = 0x65        
        (gdb) p/t i
        $26 = 1100101

五、查看内存

    可以使用examine命令(简写是x)来查看内存地址中的值。x命令的语法如下所示:    
    x/<n/f/u> <addr>     
    n
fu是可选的参数。    
    n 
是一个正整数,表示显示内存的长度,也就是说从当前地址向后显示几个地址的内容。
    f 
表示显示的格式,参见上面。如果地址所指的是字符串,那么格式可以是s,如果地十是指令地址,那么格式可以是i
    u 
表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4bytesu参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字节,g表示八字节。当我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。    
    <addr>
表示一个内存地址。

n/f/u三个参数可以一起使用。例如:    
    
命令:x/3uh 0x54320 表示,从内存地址0x54320读取内容,h表示以双字节为一个单位,3表示三个单位,u表示按十六进制显示。   

六、自动显示

    可以设置一些自动显示的变量,当程序停住时,或是在单步跟踪时,这些变量会自动显示。相关的GDB命令是display
    display <expr> 
    display/<fmt> <expr> 
    display/<fmt> <addr>    
    expr
是一个表达式,fmt表示显示的格式,addr表示内存地址,当用display设定好了一个或多个表达式后,只要你的程序被停下来,GDB会自动显示你所设置的这些表达式的值。    
    
格式is同样被display支持,一个非常有用的命令是:    
       display/i $pc    
    $pc
GDB的环境变量,表示着指令的地址,/i则表示输出格式为机器指令码,也就是汇编。于是当程序停下后,就会出现源代码和机器指令码相对应的情形,这是一个很有意思的功能。    
    
下面是一些和display相关的GDB命令:    
    undisplay <dnums...>
    delete display <dnums...>
    
删除自动显示,dnums意为所设置好了的自动显式的编号。如果要同时删除几个,编号可以用空格分隔,如果要删除一个范围内的编号,可以用减号表示(如:2-5)    
    disable display <dnums...>
    enable display <dnums...>
    disable
enalbe不删除自动显示的设置,而只是让其失效和恢复。    
    info display
    
查看display设置的自动显示的信息。GDB会打出一张表格,向你报告当然调试中设置了多少个自动显示设置,其中包括,设置的编号,表达式,是否enable

 

<think>我们已知用户想要通过gdb查看整数和浮点数在内存中的存放形式。根据之前的引用,我们知道gdb可以通过x命令查看内存,并且可以指定显示的格式。 整数和浮点数在内存中的存储方式不同:整数一般用补码,浮点数用IEEE 754标准。我们可以通过gdb的x命令以十六进制字节形式查看内存,从而观察其存储形式。 步骤: 1. 编写一个简单的C程序,包含整数和浮点数变量。 2. 使用gdb调试程序,在变量赋值后设置断点。 3. 使用gdb的x命令(examine memory)查看变量的内存表示。 示例程序(test.c): ```c #include <stdio.h> int main() { int i = 0x12345678; float f = -6.25f; return 0; // 设置断点在这里 } ``` 编译程序需要加上-g选项以包含调试信息: gcc -g -o test test.c 使用gdb调试: gdb ./test 在gdb中: (gdb) break main.c:5 // 在return 0处设置断点 (gdb) run // 运行程序到断点 (gdb) print &i // 获取变量i的地址,假设输出为0x7fffffffde24 (gdb) print &f // 获取变量f的地址 然后使用x命令查看内存: (gdb) x/4xb &i // 查看整数i的4个字节(以十六进制字节形式) (gdb) x/4xb &f // 查看浮点数f的4个字节 注意:由于系统可能是小端序,整数0x12345678在内存中会按照从低地址到高地址为0x78, 0x56, 0x34, 0x12。 浮点数-6.25的IEEE 754单精度表示为0xC0C80000(二进制:11000000110010000000000000000000),小端序存储则为00 00 C8 C0。 另外,引用[5]给出了使用x命令查看内存的例子:`x/16xb 0x7FFFFFFFE0F8`,表示从地址0x7FFFFFFFE0F8开始显示16个字节,十六进制字节格式。 因此,我们可以这样操作: 对于整数i(假设地址为addr_i): (gdb) x/4xb addr_i // 查看4个字节,十六进制格式 对于浮点数f(假设地址为addr_f): (gdb) x/4xb addr_f // 同样查看4个字节 如果想同看到变量值,可以在gdb中使用print命令打印变量值,但注意print会按照变量类型解释内存,而x命令直接查看原始内存。 此外,我们还可以利用gdb数据显示格式(如引用[1]提到)来以不同格式输出同一个变量。例如,我们可以用二进制格式查看整数: (gdb) print/t i // 以二进制形式输出整数i 但注意,直接查看内存更直接反映存储形式。 下面给出详细步骤: 步骤1:编写C程序(test.c) 步骤2:编译:gcc -g test.c -o test 步骤3:启动gdbgdb ./test 步骤4:在main函数中return 0处设置断点:break test.c:5(具体行号根据程序) 步骤5:运行:run 步骤6:当程序停在断点处,使用以下命令: p &i // 打印i的地址,记下地址,比如0x7fffffffde24 p &f // 打印f的地址,记下地址,比如0x7fffffffde20 步骤7:查看整数i的内存: x/4xb 0x7fffffffde24 // 注意替换为实际地址 预期输出(小端序):0x78, 0x56, 0x34, 0x12 步骤8:查看浮点数f的内存: x/4xb 0x7fffffffde20 // 注意替换为实际地址 预期输出(小端序):0x00, 0x00, 0xc8, 0xc0 步骤9:也可以使用其他格式查看,比如查看整数的十六进制值: p/x i // 以十六进制输出i的值,应该为0x12345678 步骤10:查看浮点数的十六进制表示(IEEE 754): p/f f // 输出浮点数值,为-6.25 p/x *(int *)&f // 将浮点数地址强制转换为整数指针,然后取整数值(即内存中的位模式),输出应为0xc0c80000(注意,这里显示的是整数形式的十六进制,但内存字节顺序是小端,所以在内存中存储为00 00 c8 c0,但转换为整数是按小端序组合的,所以整数显示为0xc0c80000?实际上这里有一个转换:小端序下,如果我们按字节从低到高是00,00,c8,c0,那么组合成32位整数就是0xc0c80000(因为低地址是低位,高地址是高位)?不对,组合后应该是0xC0C80000,但注意在x86上,当我们把这四个字节当作一个32位整数,它的值就是0xC0C80000?不对,比如我们存储的四个字节是:地址A:00, A+1:00, A+2:C8, A+3:C0,那么32位整数就是0xC0C80000(因为高字节在高地址,低字节在低地址,所以整数是0xC0C80000)。但是,我们使用x/4xb看到的是从低地址到高地址的字节序列:00 00 C8 C0,所以当我们用整数解释,这个值就是0xC0C80000(因为C0在最高地址,作为最高字节)。所以用p/x *(int*)&f会显示0xC0C80000。 实际上,上面的步骤10可以这样验证浮点数的内存表示,但注意类型转换可能在不同编译器下有差异。 因此,总结查看内存存放形式的方法: 对于整数:直接使用x命令查看其地址处的字节,或者使用print以十六进制输出整数值(但注意print输出的整数值是经过转换的,而内存字节顺序需要单独考虑)。 对于浮点数:同样使用x命令查看字节,或者使用强制类型转换将其内存解释为整数后再输出(这样可以看到整个位模式)。 注意:由于gdb的x命令默认显示的是字节序列(小端序),所以我们看到的是内存中的实际排列。而如果使用print打印整数值(强制转换后的),则直接得到的是该地址处按小端序组合的整数。 因此,我们可以提供两种方法: 方法一:使用x命令直接查看内存字节(推荐,因为最直接)。 方法二:使用强制类型转换后打印整数形式(这样可以看到整个位模式的十六进制值)。 下面按照要求组织回答: </think>### 使用 GDB 查看整数和浮点数内存存放形式的方法 #### 📌 核心步骤概览 1. **编译调试版本程序**:使用 `gcc -g` 编译包含整数和浮点变量的代码 2. **启动 GDB 调试**:加载可执行文件并设置断点 3. **查看内存地址**:获取目标变量的内存地址 4. **使用 `x` 命令解析内存**:按字节查看原始内存数据 5. **结合数据格式解析**:根据类型解释内存内容 --- ### 🔍 详细操作指南 #### 步骤 1:准备测试代码 ```c #include <stdio.h> int main() { int num = 0x12345678; // 整型常数示例(十六进制) float f = -6.25f; // 浮点常数示例 return 0; // 在此设断点 } ``` 编译添加调试信息: ```bash gcc -g -o mem_test mem_test.c ``` --- #### 步骤 2:启动 GDB 调试 ```bash gdb ./mem_test (gdb) break 6 # 在return语句设断点[^4] (gdb) run ``` --- #### 步骤 3:获取变量地址 ```bash (gdb) print &num $1 = (int *) 0x7fffffffde24 # 记录整型地址 (gdb) print &f $2 = (float *) 0x7fffffffde20 # 记录浮点地址 ``` --- #### 步骤 4:查看整型内存布局 使用 `x/4xb` 命令查看 4 字节内存(十六进制字节格式): ```bash (gdb) x/4xb 0x7fffffffde24 0x7fffffffde24: 0x78 0x56 0x34 0x12 ``` 🔎 解析: - 小端序存储(低位在前):`78 56 34 12` → 实际值 `0x12345678` - 内存布局验证: | 地址偏移 | 值 | 含义 | |----------|--------|----------------| | +0 | `0x78` | 最低有效字节 | | +1 | `0x56` | | | +2 | `0x34` | | | +3 | `0x12` | 最高有效字节 | --- #### 步骤 5:查看浮点内存布局 ```bash (gdb) x/4xb 0x7fffffffde20 0x7fffffffde20: 0x00 0x00 0xc8 0xc0 ``` 🔎 解析(IEEE 754 单精度): 1. **原始字节**:`00 00 c8 c0`(小端序)→ 实际存储 `0xC0C80000` 2. **二进制拆分**: ```binary 1 10000001 10010000000000000000000 ┬ ──────── ─────────────────────── │ │ │ │ │ └─ 尾数段 (23bit): 0.1001 │ └─ 指数段 (8bit): 129 - 127 = 2 └─ 符号位 (1bit): 1(负数) ``` 3. **值计算**: $$ \begin{align*} \text{值} &= (-1)^1 \times (1 + 2^{-1}) \times 2^2 \\ &= -1 \times 1.5 \times 4 \\ &= -6.0 \quad \text{❌ 需修正} \end{align*} $$ ✅ 正确解析:尾数实际为 $1.1001_2 = 1 + 0.5 + 0.0625 = 1.5625$ $$ -1 \times 1.5625 \times 4 = -6.25 $$ --- #### 💡 高级技巧:直接查看位模式 ```bash (gdb) print/x *(int*)&f # 将float内存解释为整数[^2] $3 = 0xc0c80000 (gdb) print/t *(int*)&f # 二进制格式输出[^1] $4 = 110000001100100000000000000000 ``` --- ### 📊 关键命令总结 | 目的 | GDB 命令 | 输出示例 | |-------------------------|--------------------------------|------------------------------| | 查看整型内存 | `x/4xb <int_address>` | `0x78 0x56 0x34 0x12` | | 查看浮点内存 | `x/4xb <float_address>` | `0x00 0x00 0xc8 0xc0` | | 浮点位模式(十六进制) | `print/x *(int*)&<float_var>` | `0xc0c80000` | | 浮点位模式(二进制) | `print/t *(int*)&<float_var>` | `1100000011001000...` | > ⚠️ **注意事项** > 1. 字节顺序取决于 CPU 架构(x86/x64 为小端序) > 2. 浮点解析需严格遵循 IEEE 754 标准[^2] > 3. 指针转换仅用于查看内存布局,勿用于实际计算
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值