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

SomeStruct getSomeStruct()
{
    SomeStruct input;

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

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

有帮助吗?

解决方案

  

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

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

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

还有一些优化,例如<!> quot;命名值返回优化<!> quot;可以省略额外的副本。因此,如果您使用了缺少的SomeStruct,结果将直接在调用者分配的空间上创建,而不是创建临时文件并将其复制。

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

其他提示

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

一旦打开警告,您还应该使用<=>将警告视为错误,并在检测到错误时停止构建。

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

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

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

我觉得这很有趣。使用默认选项时,以下编译器在编译时具有以下行为 GetSomeStruct() 功能:

  • Microsoft VC,所有版本(至少从 VC6 开始):

    error C4716: 'getSomeStruct' : must return a value

  • 数字火星:

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

  • 科莫:

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

  • 海湾合作委员会:

    没有错误或警告

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

返回语句不带 表达式只能在 不返回值的函数、 即返回值为 类型 void、构造函数 (12.1) 或 析构函数 (12.4)。...流走 相当于 返回值为零;这一结果 中的未定义行为 返回值函数。

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

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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top