鉴于下面的代码,编译器为函数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; }
答案 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
。)
在我的海湾合作委员会这里是我印刷的内容:
f1
:hello1,8??q?
(逗号后面的一些随机字符)f2
:hello2,8j?y5
(逗号后面的一些随机字符)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*
,它是(静态)const char [7]
。在代码const char* const& r = "hello3";
中,引用不能直接绑定到此char数组,因为它的类型不同,因此编译器必须创建一个临时 char指针(在堆栈)通过绑定引用的隐式转换(数组到指针衰减)初始化(demo)。此临时const char*
的生命周期“延伸”到引用r
的生命周期,因此不会在第一个分号结束,而是在函数返回时结束(demo和{ {3}})。因此f3
会返回“悬空参考”。在我的测试输出代码中,任何覆盖堆栈的后续操作都会使UB可见。
编辑:我意识到“它在第二个输出上打印垃圾”不是UB的证明。具有UB的程序也可以完全按预期工作,或者崩溃,或什么都不做,或者其他什么。但是,尽管如此,我不相信一个定义明确的程序(没有UB)会像那样打印垃圾......
答案 1 :(得分:1)
是的,代码的行为是未定义的,因为它返回对本地指针的引用,该引用已在f1
和f2
中正确检测到。
您不能依赖编译器的诊断来捕获这些(或任何其他)未定义行为的情况,它们是在“尽力而为”的基础上提供的。在这个简单的例子中,g ++ 4.8.0没有警告(使用-Wall
)显示编译器很容易被欺骗:
int& r() {
int x = 1;
int& y = x;
return y;
}
(刚刚返回x
按预期发出警告,并clang
警告所有四个功能。)