首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我感觉下面的f3()展示了UB。但是我怎么能确定这一点呢?

我感觉下面的f3()展示了UB。但是我怎么能确定这一点呢?
EN

Stack Overflow用户
提问于 2013-07-02 01:08:38
回答 2查看 129关注 0票数 1

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

代码语言:javascript
复制
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; }
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-07-02 04:37:25

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

对于那些坚持认为f3不是UB (甚至不是f1/f2)的人:请您尝试运行以下代码:

代码语言:javascript
复制
#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上,这是我得到的信息:

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

对我来说那看起来很像UB。

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

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

代码语言:javascript
复制
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的生命周期,因此不会在第一个分号结束,而是在函数返回(demooutput with all optimizations off)时结束。所以f3会返回一个“悬空引用”。在我的测试输出代码中,任何覆盖堆栈的后续操作都会使UB可见。

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

票数 3
EN

Stack Overflow用户

发布于 2013-07-02 02:10:18

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

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

代码语言:javascript
复制
int& r() {
    int x = 1;
    int& y = x;
    return y;
}

(正如预期的那样,只需返回x就会发出警告,而clang会对所有四个函数发出警告。)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17409938

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档