当返回对象的函数在没有return语句的情况下结束时会发生什么

Mat*_*son 16 c++ return function object language-lawyer

在C++中,当一个应该返回一个对象的函数在没有return语句的情况下结束时会发生什么?什么回来了?

例如

std::string func() {}
Run Code Online (Sandbox Code Playgroud)

son*_*yao 28

什么回来了?

我们不知道.根据标准,行为是不确定的.

§6.6.3/ 2返回语句[stmt.return]:

(强调我的)

在构造函数,析构函数或具有cv void返回类型的函数的末尾流动,相当于return没有操作数的函数.否则,流出除main(basic.start.main)之外的函数的末尾会导致未定义的行为.

事实上,大多数编译器会给它一个警告,比如Clang:

警告:控制到达非空函数的末尾[-Wreturn-type]

  • @MattMunson没有.实际上,返回值要么包含在寄存器中,要么指向一个寄存器.在前一种情况下,寄存器可能包含也可能不包含正确的值.在后一种情况下,指针将指向错误的位置,或指向正确的(但未初始化的)内存.无论哪种方式,都很糟糕. (6认同)

πάν*_*ῥεῖ 7

在C++中,当一个应该返回一个对象的函数在没有return语句的情况下结束时会发生什么?

它会导致未定义的行为.没有人能分辨出究竟会发生什么.

  • @immibis尽管如此,它确实意味着"不可能预测会发生什么". (6认同)
  • 回复:"没有人能分辨出究竟会发生什么":不,这被夸大了.这意味着**只****语言定义**没有告诉你发生了什么.您的编译器文档可能会告诉您. (4认同)

isa*_*nae 5

我很好奇,所以我在Visual C++ 2015上做了一些测试.

int f()
{
    if (false)
        return 42;

    // oops
}

int main()
{
    int i = f();
}
Run Code Online (Sandbox Code Playgroud)

我不得不添加if以获取警告而不是硬错误:

> cl /nologo /FAs /c a.cpp
a.cpp(6) : warning C4715: 'f': not all control paths return a value
Run Code Online (Sandbox Code Playgroud)

生成的汇编代码非常简单,我删除了不相关的部分.这是以下内容f():

f:
    xor eax, eax
    je label
    mov eax, 42
label:
    ret
Run Code Online (Sandbox Code Playgroud)

xor条线基本上是eax=0.因为if (false)是一个常量条件,所生成的代码甚至都不打算进行比较,然后无条件地跳转到label,只是从函数返回.你可以看到"返回值"(42)实际上会被存储eax,但是这行不会被执行.因此,eax == 0.

这是做什么的main():

    call f
    mov _i$[ebp], eax
    ret
Run Code Online (Sandbox Code Playgroud)

它调用f()并盲目地复制eax到堆栈中的某个位置(在哪里i).因此,i == 0.

让我们尝试使用对象和构造函数更复杂的东西:

struct S { int i=42; };

S f()
{
    if (false)
        return {};

    // oops
}

int main()
{
    S s = f();
}
Run Code Online (Sandbox Code Playgroud)

main()基本上什么是sizeof(S)堆栈上的保留字节,将第一个字节的地址放入eax然后调用f():

    lea eax, _s$[ebp]
    push eax
    call f
Run Code Online (Sandbox Code Playgroud)

同样,f()不会做任何事情,因为它会无条件地跳到函数的末尾:

f:
    xor eax, eax
    je label
    ; some stuff
    ; call S::S(), which would set i to 42
    ; but none of that will happen
label:
    ret
Run Code Online (Sandbox Code Playgroud)

那么sizeof(S)主要的字节发生了什么?他们从未改变过.它们包含该特定位置已经存在的内容.它们含有垃圾.

对于给定编译器的给定版本,这是一个未经优化的构建.更改编译器,更改行为.启用优化程序,彻底更改行为.

不要这样做.