【发布时间】:2021-04-29 18:17:59
【问题描述】:
#include <stdio.h>
typedef struct node {
int val;
} node;
node *copy(node *x) {
node *tmp = &((node){.val = 10});
return tmp;
}
int main() {
// node *a = &((node){.val = 9});
node *a = 0;
a = copy(a);
printf("%d\n", a->val);
return 0;
}
据我所知,函数堆栈中的每个对象在离开函数后都将无法访问。 但是,它没有。
我使用 GDB 来检查对象在哪里。
(gdb) b 9
(gdb) b 16
(gdb) r
(gdb) p $rsp
$8 = (void *) 0x7fffffffdcc0
(gdb) p $rbp
$9 = (void *) 0x7fffffffdcf0
(gdb) p tmp
$7 = (node *) 0x7fffffffdcdc
(gdb) c
(gdb) p $rsp
$10 = (void *) 0x7fffffffdd00
(gdb) p $rbp
$11 = (void *) 0x7fffffffdd10
(gdb) p a
$12 = (node *) 0x7fffffffdcdc
您可以看到变量“a”指向0x7fffffffdcdc,它不在$rsp 和$rbp 之间。
最后程序打印“10”,没有任何错误信息。
我不明白为什么它可以工作。
【问题讨论】:
-
程序有未定义的行为。您可以创建多种具有未定义行为的程序,它们将按预期工作。还要注意函数参数x没有使用。
-
参考:C11 6.5.2.5p5:“[...] 复合文字 [...] 具有与封闭块关联的自动存储持续时间。”
-
从语言的角度来看,程序具有未定义的行为,未定义行为的可观察效果可以是任何东西,包括程序按预期工作的外观。从实际目的来看,在这么短的程序中,用于保存现已失效对象的内存在对象被销毁后并没有被重用,因此您仍然可以查看该内存并找到您期望的数据。尝试在
copy之后但在打印值之前调用另一个不相关的函数。 -
我使用
gcc -O0 -g -std=c99 -Wall -Werror -fsanitize=undefined main.c来检测未定义的行为,但标准输出为空。 -
静态分析不会捕获所有未定义行为的实例 - 这需要更深入的分析。碰巧
-O3会执行如此深入的分析,并且如果您设置问题已被捕获。如果您想将编译器用作穷人的静态分析工具,请始终使用高优化级别。node y = {.val = 10} ; return &y;等更简单的成语在-O0中被捕获。在-O3它被没有sanitize捕获。优化器是比sanitize更好的静态分析工具(至少在这方面)。
标签: c pointers struct lifecycle