我已经在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?
为什么{1}在{1}中显示为ptr
?
调用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)行的inp
中变为NULL
时,它如何反映在func1()中?
答案 0 :(得分:2)
GDB从堆栈构造调用堆栈,并且由于inp
(func2
的参数)为0,因此GDB假定传递的参数为0,因此它表示{{用0调用1}}。
崩溃时的堆栈是这样的:
func2
答案 1 :(得分:2)
在C中调用函数时,参数将复制到寄存器中或压入堆栈。被调用的函数可以出于任何目的重用这些寄存器和堆栈位置。通常,但并非总是如此,在函数调用的整个生命周期中,参数都保存在同一寄存器或堆栈位置。
在32位系统上,函数的第一个参数 - 在您的情况下为inp
- 通常位于堆栈中,距离堆栈帧的基指针指向的位置为12个字节。请参阅stackoverflow.com: what exactly is program stack's growth direction。
当gdb
执行回溯时,它从编译器获得的唯一指导就是“func2
的第一个参数被命名为inp
,并且是一个4字节的类型值*UTYPE
位于距离%ebp寄存器“。
如果在func2
中某处,您改变inp
,就像在位置(4)处所做的那样,那么从该点开始的任何回溯都可以很好地显示inp
的更改值,在你的情况下,0 inp
输入func2
时的值永远丢失,除非编译器足够聪明,包括像func2
的第一个参数那样的指导名为inp
并且是类型*UTYPE
的4字节值,通过将堆栈展开到前一帧并查看{{func2
的值,可以找到它在ptr
上的值。 1}},位于与%ebp寄存器相差4字节的位置。“我相信较新版本的DWARF调试格式可以指定类似的东西。
我无法解释为什么gdb
的回溯在ptr
的框架中显示为func1
的值为0.将inp
设置为NULL应该对ptr
的价值和gdb显示ptr
价值的能力。