如果你没有在C ++中返回一个值,会发生什么?

时间:2008-11-16 04:35:06

标签: c++ g++ return-value

昨天,我发现自己编写了这样的代码:

SomeStruct getSomeStruct()
{
    SomeStruct input;

    cin >> input.x;
    cin >> input.y;
}

当然忘记实际返回我刚创建的结构。奇怪的是,这个函数返回的 结构中的值被初始化为零(当使用g ++编译时)。这只是一个巧合还是另一个SomeStruct被隐式创建和初始化?

6 个答案:

答案 0 :(得分:20)

声明返回值的函数(不显式返回值)的结尾会导致未定义的后果。对于gcc,您应该从-Wall命令行开关开始,该开关打开最有用的警告。控制所需警告的特定gcc警告是-Wreturn-type(包含在-Wall中,我只是提到这一点是为了完整性)。

启用警告后,您还应使用-Werror将警告视为错误,并在检测到错误时停止构建。

答案 1 :(得分:8)

大多数现代CPU架构的调用约定指定一个特定的寄存器,以将函数返回值传递回调用者。调用者进行函数调用,然后使用指定的寄存器作为返回值。如果您没有显式返回值,则调用者仍将使用该寄存器中的任何垃圾。

编译器还将使用它可用于函数内部计算的所有寄存器。指定用于保存返回值的寄存器也将用于函数内的misc计算。因此,当您忘记指定返回值时,发现奇迹般地将错误返回给调用者的情况并不罕见:编译器使用该寄存器来存储您的对象。

不幸的是,即使对函数进行微不足道的更改也会导致寄存器分配发生变化,因此返回值将变为真正的垃圾。

答案 2 :(得分:7)

  

是否隐式创建并初始化了另一个SomeStruct?

考虑如何返回结构。如果xy都是32位,则它太大而不适合32位架构上的寄存器,这同样适用于64位架构上的64位值(@ Denton Gentry的回答提到了返回的值是多么简单),因此必须在某处分配。为此使用堆将是浪费,因此必须在堆栈上分配它。但它不能位于getSomeStruct函数的堆栈帧上,因为在函数返回后它不再有效。

编译器通过向调用函数传递一个隐藏指针指向分配给它的空间,调用者告诉被调用函数将结果放在哪里(可能是调用者堆栈的某个位置)。因此,将其设置为零的位置在调用者上,而不是在getSomeStruct函数上。

还有一些优化,例如“命名值返回优化”,可以省略额外的副本。所以,如果你使用了缺失的return,结果将直接在调用者分配的空间上创建,而不是创建临时的并复制它。

要了解更多有关正在发生的事情,您必须查看调用者函数。它是否正在初始化(为零)一个“空”SomeStruct,您稍后会为其分配getSomeStruct函数的返回值?或者它正在做其他事情?

答案 3 :(得分:3)

我发现这很有趣。使用默认选项时,以下编译器在编译GetSomeStruct()函数时具有以下行为:

  • Microsoft VC,所有版本(无论如何都是VC6):

    <强> error C4716: 'getSomeStruct' : must return a value

  • 数字火星:

    <强> Warning 18: implied return of getSomeStruct at closing '}' does not return value

  • Comeau:

    <强> warning: missing return statement at end of non-void function "getSomeStruct"

  • gcc:

    没有错误或警告

鉴于标准中的以下几句话(6.6.3第2段):

  

没有的退货声明   表达式只能用于   不返回值的函数,   也就是说,返回一个函数   type void,构造函数(12.1)或a   析构函数(12.4)。 ......流了下来   函数的结尾相当于   没有价值的回报;这个结果   在未定义的行为中   价值回归功能。

我想说在这种情况下,编译器没有理由不给出错误。为什么这么多编译器只给出警告或根本没有诊断?

答案 4 :(得分:2)

对我来说,编译器不允许它:http://codepad.org/KkzVCesh

答案 5 :(得分:1)

您没有收到任何警告,因为您没有启用-Wall -Werror。 (如其他答案所述)

但是我认为你可能得到了一个零填充结构,因为堆栈对象是在调用函数中默认构造的,可能带有显式零参数,或者由于堆栈上的零而导致?