首页 > 解决方案 > 仅在使用 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;
}

标签: cgccprintfcompiler-optimization

解决方案


GCC 看到它Boo返回一个指向局部变量的指针,因此任何使用这个指针的尝试都是未定义的行为。这意味着,根据 C 标准,编译器可以为所欲为,在这种情况下,GCC 经常会生成“高效”的代码,即使它与程序员的意图完全无关。它内联了对 的调用Boo,它没有可见的效果,因此被优化掉了,它选择了一种“有效”的方式来为 提供一个参数printf,它恰好是常数 0。

参见神螺栓。的整数参数printfgo in esi,它来自ebp它被归零。我想有一个错过的优化,因为它在ebp调用printfto reload into时保存了相同的 0 esi,而不是之后重新归零esi。但整个分析有点毫无意义,因为行为是未定义的。

Boo它本身被优化为一个只返回一个NULL指针而不做其他任何事情的函数,由于未定义的行为,这再次是合法的,但Boo程序不调用该版本。 Foo也被优化为一个无需递归立即返回的函数;编译器可以判断递归调用无效。(如果将负参数传递给Foo,由于有符号整数溢出,您将有未定义的行为,因此编译器无需处理这种情况。)


推荐阅读