c - 仅在使用 gcc 编译器进行优化时,为什么当我错误地返回指向堆栈上的值的指针时 printf 打印 0?
问题描述
我试图了解此示例中 printf 的行为。当然这里的主要问题是我们正在返回一个指针,该指针指向在函数 Boo 返回后弹出的堆栈上的值。
我用 gcc 编译。在 test1 中:我得到了 7 打印两次,这是预期的。以及 test2 中第二个 printf 上的垃圾值。但是当我使用 gcc -O3 编译时,两种情况下都打印了 0并且编译器警告关于返回局部变量的地址。
test.c:在函数'Foo'中:test.c:8:12:警告:函数返回局部变量的地址 [-Wreturn-local-addr] return t; ^ test.c:5:9: 注意:此处声明为 int j;
有人可以帮我解释导致这种行为的 printf 行为如何吗?
int *Boo(int i, int *p)
{
int j;
int *t = &j;
*t = i + *p;
return t;
}
void Foo(int x)
{
if (x == 0) { return;}
Foo(x - 1);
}
//test1
int main(void)
{
int x = 5;
int *t = Boo(2, &x);
printf("%d", *t);
printf("%d", *t);
return 0;
}
//test2
int main(void)
{
int x = 5;
int *t = Boo(2, &x);
printf("%d", *t);
Foo(8);
printf("%d", *t);
return 0;
}
解决方案
GCC 看到它Boo
返回一个指向局部变量的指针,因此任何使用这个指针的尝试都是未定义的行为。这意味着,根据 C 标准,编译器可以为所欲为,在这种情况下,GCC 经常会生成“高效”的代码,即使它与程序员的意图完全无关。它内联了对 的调用Boo
,它没有可见的效果,因此被优化掉了,它选择了一种“有效”的方式来为 提供一个参数printf
,它恰好是常数 0。
参见神螺栓。的整数参数printf
go in esi
,它来自ebp
它被归零。我想有一个错过的优化,因为它在ebp
调用printf
to reload into时保存了相同的 0 esi
,而不是之后重新归零esi
。但整个分析有点毫无意义,因为行为是未定义的。
Boo
它本身被优化为一个只返回一个NULL
指针而不做其他任何事情的函数,由于未定义的行为,这再次是合法的,但Boo
程序不调用该版本。 Foo
也被优化为一个无需递归立即返回的函数;编译器可以判断递归调用无效。(如果将负参数传递给Foo
,由于有符号整数溢出,您将有未定义的行为,因此编译器无需处理这种情况。)
推荐阅读
- mongodb - 如何在 MongoDB 中设置哈希索引的哈希表的大小?
- firebase - Firebase Cloud Functions 在云函数中设置环境配置
- angular - Angular 6+中基于角色的重定向
- javascript - 如何更改剑道日期选择器的最小日期:Jquery
- ffmpeg - FFMPEG:如何将 image2 格式输出到 tcp/udp 套接字?
- npm - 为材料设计安装模块的 NPM 权限错误
- html - 当我恢复网络浏览器时,对齐方式发生变化或重叠
- python - 如何使用具有独立读写指针的StringIO?
- python-3.x - 如果字符串的所有字符都是唯一的,则打印 Yes 否则打印 No
- angular - 如何初始化@Input?