我已经专业地使用 C 编写代码了一段时间,但仍然被一些与指针相关的问题所困扰。我非常感谢 SO 社区帮助理解以下问题。
以下代码崩溃并生成核心文件。
void func1() // Frame 1 in GDB stack trace.
{
UTYPE *ptr; // pointer to user defined type
...
// data is of type UTYPE and has valid contents.
// lets say its address is 0x100
ptr = &data; --- (1)
...
func2(ptr); --- (2)
...
}
void func2(UTYPE *inp) // Frame 0 in GDB stack trace.
{
if(!inp) --- (3)
return;
...
// another_ptr is of UTYPE * which is a NULL.
inp = another_ptr; ---- (4)
/* Did not check for NULL and dereference inp and CRASH */ ---- (5)
}
来自 GDB 的简化回溯:
Frame 0:
func2(inp = 0x0)
// crash at line (5) due to dereference
Frame 1:
func1: func2(0x0)
// `ptr` at line (2) is 0x0. Why is this so?
为什么 ptr
在第 1 帧中显示为 0x0 (NULL)
?
当 func2()
被调用时,其调用堆栈如下所示:
| //local vars |
| |
| another_ptr = |
| NULL |
+---------------+
| return addr |
+---------------+
| input args |
| copy of ptr |
| contents |
| 0x100 |
对于func1()
,它的调用栈应该是这样的:
| |
| ptr = 0x100 |
| |
+---------------+
| return addr |
+---------------+
| input args |
| none in this |
| func |
当第(4)行func2()
中的inp
变为NULL
时,在func1()中是如何体现的呢?
最佳答案
当在 C 中调用函数时,参数被复制到寄存器或压入堆栈。被调用函数可以出于任何目的重用这些寄存器和堆栈位置。通常,但并非总是如此,在函数调用的整个生命周期中,参数都保存在相同的寄存器或堆栈位置。
在 32 位系统上,函数的第一个参数 - 在您的例子中是 inp
- 通常位于堆栈上,距离堆栈帧的基指针指向的位置 12 个字节到。参见 stackoverflow.com: what exactly is program stack's growth direction .
当 gdb
执行回溯时,它从编译器获得的唯一指导是类似“func2
的第一个参数被命名为 inp
并且是 *UTYPE
类型的 4 字节值,位于距 %ebp 寄存器 12 字节的偏移量处。
如果在 func2
的某处,您更改了 inp
,就像您在位置 (4) 所做的那样,那么从该点开始的任何回溯都可能很好地显示更改后的值inp
的值,在您的情况下为 0。输入 func2
时 inp
的值将永远丢失,除非编译器足够聪明包括诸如“func2
的第一个参数名为 inp
并且是 *UTYPE
类型的 4 字节值及其在输入时的值之类的指导func2
可以通过将堆栈展开到前一帧并查看 ptr
的值来找到,它位于距 %ebp 寄存器 -4 字节的偏移处”我相信较新版本的 DWARF 调试格式可以指定这样的内容。
我无法解释为什么你的 gdb
的回溯在 func1
的框架中显示 ptr
的值为 0。设置 inp
到 NULL 应该对 ptr
的值和 gdb 显示 ptr
的值的能力没有影响。
关于c - 通过检查核心和调用堆栈了解使用 GDB 的 C 指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21466160/