1

鉴于编译器下面的代码warning C4172: returning address of local variable or temporary为函数 f1() 和 f2() 发出,但不为 f3() 发出。我了解编译器在某些情况下可能无法识别此问题,因为下面的函数 f3() 似乎就是这种情况。但是,在这种情况下,如果没有警告信息,我怎么能确定正确的诊断呢?

const char* const& f1() { return "hello1"; }
const char* const& f2() { return static_cast<const char*>("hello2"); }
const char* const& f3() { const char* const& r = "hello3"; return r; }
4

2 回答 2

3

我确信所有三个函数都有未定义的行为。

对于坚持f3不是 UB(甚至f1/ f2)的人:请您尝试运行此代码:

#include <iostream>

const char* const& f1() { return "hello1"; }
const char* const& f2() { return static_cast<const char*>("hello2"); }
const char* const& f3() { const char* const& r = "hello3"; return r; }

int main()
{
    using namespace std;

//#define F f1
//#define F f2
#define F f3

    const char* const& ret = F();
    cerr << ret;
    cerr << ",";
    cerr << ret;

    return 0;
}

(我使用cerr而不是cout立即冲洗。您可以更改cerr为并在第二个输出之后cout添加一个。)cout << flush;ret

在我的 GCC 上,这是我打印的内容:

  • with f1:(hello1,8??q?逗号后的一些随机字符)
  • with f2:(hello2,8j?y5逗号后的一些随机字符)
  • with f3:(hello3,,逗号后的第二个逗号)

对我来说,这看起来很像UB......

(注意:如果我删除任何一个,const&那么它“有效”。const&真正删除的当然是返回类型中的一个。)

我认为那是因为发生的f3事情是这样的:

const char* const& f3()
{
    const char* __tmp001 = &("hello3"[0]); // "array decaying"
    const char* const& r = __tmp001;
    return r;
}

实际上,字符串文字"hello3"不是a ,而是const char*a (static) const char [7]。在代码const char* const& r = "hello3";中,引用不能直接绑定到这个char数组,因为它没有相同的类型,所以编译器必须创建一个临时的char指针(创建在堆栈上),通过隐式转换(array-to-pointer衰减) 引用绑定到的 ( demo )。这个临时的生命周期const char*被“延长”到引用的生命周期r,因此不会在第一个分号处结束,而是在函数返回时结束(演示输出关闭所有优化)。所以f3返回一个“悬空引用”. 在我的测试输出代码中,任何覆盖堆栈的后续操作都会使 UB 可见。

在 jalf 的评论之后编辑:我意识到“它在第二个输出上打印垃圾”不是 UB 的证明。带有 UB 的程序也可以完全按预期工作,或者崩溃,或者什么也不做,或者其他什么。但是,尽管如此,我不相信一个定义明确的程序(没有 UB)会打印这样的垃圾......

于 2013-07-01T20:37:24.573 回答
1

是的,代码的行为是未定义的,因为它返回对本地指针的引用,对于f1and可以正确检测到该引用f2

您不能依靠编译器的诊断来捕获这些(或任何其他)未定义行为的情况,它们是在“尽力而为”的基础上提供的。-Wall在这个简单的例子中,g++ 4.8.0 没有警告(带)表明编译器很容易被愚弄:

int& r() {
    int x = 1;
    int& y = x;
    return y;
}

(只是x按预期返回警告,并clang警告所有四个功能。)

于 2013-07-01T18:10:18.287 回答