Nam*_*rma 5 c gdb null-pointer
我正在调试崩溃,其中我们有一个类似于 - 的代码片段
1184 static void
1185 xyz_delete (<struct type1> *c, <struct type2> **a)
1186 {
...
...
...
...
1196 b = *a;
1197 if (!b) {
1198 return;
1199 }
...
...
1203 prev = b->next;
1204 b->next = NULL;
...
...
1245 free_timer(b->active_timer);
...
...
...
}
Run Code Online (Sandbox Code Playgroud)
我们碰巧看到了崩溃——分段错误;其调用堆栈如下所示 -
#1 0x456789123 in __free [__be___free] (ptr=<optimized out>, saved_caller_pc=0x123456789 , attr=0x0) at free.c:1234
#2 0x345678912 in xyz_delete [__be_xyz_delete...] (c=c@entry=0x234567891, a=a@entry=0x0) at myfile.c:1245
#3 0x455678912 in abc (apple=0x52453545, a=<optimized out>, hello=12) at myfile:1312
Run Code Online (Sandbox Code Playgroud)
从调用堆栈中,我们可以注意到传递给函数 xyz_delete 的第二个参数 a 为 NULL。然而,当我们在第 1196 行取消引用 a 时,没有发生崩溃 - 这真是令人惊讶!并且在第 1203 行和第 1204 行对 b 执行的读写操作很少。但是当在第 1245 行对 b->active_timer 调用 free_timer 时,会出现分段错误。 free_timer 又调用 free。
如何取消引用 NULL 指针而不导致崩溃?
对于这里可能发生的事情有任何合乎逻辑的解释吗?
C11 标准规定:
一元 * 运算符表示间接。如果操作数指向函数,则结果是函数指示符;如果它指向一个对象,则结果是指定该对象的左值。如果操作数的类型为“指向类型的指针”,则结果的类型为“type”。如果为指针分配了无效值,则一元 * 运算符的行为未定义。
[...]
通过一元 * 运算符取消引用指针的无效值包括空指针、 [...]
[6.5.3.2 地址和间接运算符,C11]
因此,取消引用空指针是一种未定义的行为。请注意,未定义的行为不一定会导致崩溃,并且可以被编译器忽略。
可能的未定义行为包括完全忽略具有不可预测结果的情况,到在翻译或程序执行期间以环境特征的记录方式表现(无论是否发出诊断消息),到终止翻译或执行(发出诊断消息)。
[3.4.3 未定义行为,C11]
例如,某些优化可以跳过指针间接寻址并防止程序崩溃。
在取消引用指针之前,您需要检查指针是否无效,否则您的程序将调用未定义的行为并且可能是不可预测的。启用-Wnull-dereference可以帮助您实现这一点,但它可能无法捕获所有内容。