给定下面的代码,编译器为函数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; }发布于 2013-07-02 04:37:25
我确信这三个函数都有未定义的行为。
对于那些坚持认为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,并在ret的第二个输出之后添加一个cout << flush;。)
在我的GCC上,这是我得到的信息:
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"不是const char*,它是一个(静态) const char [7]。在代码const char* const& r = "hello3";中,引用不能直接绑定到这个字符数组,因为它不具有相同的类型,因此编译器必须创建一个临时字符指针(在堆栈上创建),该指针由绑定了引用的隐式转换(数组到指针衰减)初始化(demo)。这个临时const char*的生命周期被“扩展”到引用r的生命周期,因此不会在第一个分号结束,而是在函数返回(demo和output with all optimizations off)时结束。所以f3会返回一个“悬空引用”。在我的测试输出代码中,任何覆盖堆栈的后续操作都会使UB可见。
在jalf的评论之后编辑:我意识到“它在第二个输出上打印垃圾”不是UB的证据。使用UB的程序可以完全按照预期工作,或者崩溃,或者什么都不做,等等。但是,尽管如此,我不相信一个定义良好的程序(没有UB)会像这样打印垃圾……
发布于 2013-07-02 02:10:18
是的,代码的行为是未定义的,因为它返回了对本地指针的引用,这在f1和f2中是正确检测到的。
您不能依赖编译器的诊断来捕获这些(或任何其他)未定义行为的情况,它们是在“尽力”的基础上提供的。在这个简单的例子中,g++ 4.8.0 not warning (使用-Wall)显示了编译器很容易被愚弄:
int& r() {
int x = 1;
int& y = x;
return y;
}(正如预期的那样,只需返回x就会发出警告,而clang会对所有四个函数发出警告。)
https://stackoverflow.com/questions/17409938
复制相似问题