我有一种感觉f3()下面展示了UB。但我怎么能确定这个呢?

时间:2013-07-01 17:08:38

标签: c++ undefined-behavior

鉴于下面的代码,编译器为函数f1()和f2()发出warning C4172: returning address of local variable or temporary,但不为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; }

2 个答案:

答案 0 :(得分: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。)

在我的海湾合作委员会这里是我印刷的内容:

  • f1hello1,8??q?(逗号后面的一些随机字符)
  • f2hello2,8j?y5(逗号后面的一些随机字符)
  • f3hello3,,(逗号后面的第二个逗号)

这对我来说非常像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*,它是(静态)const char [7]。在代码const char* const& r = "hello3";中,引用不能直接绑定到此char数组,因为它的类型不同,因此编译器必须创建一个临时 char指针(在堆栈)通过绑定引用的隐式转换(数组到指针衰减)初始化(demo)。此临时const char*的生命周期“延伸”到引用r的生命周期,因此不会在第一个分号结束,而是在函数返回时结束(demo和{ {3}})。因此f3会返回“悬空参考”。在我的测试输出代码中,任何覆盖堆栈的后续操作都会使UB可见。

在jalf的评论之后

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

答案 1 :(得分:1)

是的,代码的行为是未定义的,因为它返回对本地指针的引用,该引用已在f1f2中正确检测到。

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

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

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