29

我刚刚遇到的一些旧代码:

MLIST * new_mlist_link()
{
    MLIST *new_link = (MLIST * ) malloc(sizeof(MLIST));
    new_link->next  = NULL;
    new_link->mapi  = NULL;
    new_link->result = 0;
}

这被调用来构建一个链表,但是我注意到没有声明:

return new_link;

即使没有 return 语句,列表仍然可以正确构建。为什么会这样?

编辑:平台:Mandriva 2009 64bit Linux 2.6.24.7-server GCC 4.2.3-6mnb1

编辑:有趣...此代码还在大约 5 个不同的 Linux 安装、所有不同的版本/风格以及 Mac 上成功运行。

4

8 回答 8

35

在 32 位 Windows 上,大多数时候,函数的返回值留在 EAX 寄存器中。其他操作系统也使用了类似的设置,当然它是特定于编译器的。这个特定的函数可能将 new_link 变量存储在同一位置,因此当您返回时没有返回,该位置中的变量被调用者视为返回值。

这是不可移植的,并且实际上这样做非常危险,但也是使 C 编程如此有趣的小事之一。

于 2010-05-05T20:24:27.030 回答
7

可能它只是使用了 EAX 寄存器,该寄存器通常存储被调用的最后一个函数的返回值。这根本不是一个好习惯!这类事情的行为是不确定的..但是看到工作很酷;-)

于 2010-05-05T20:26:34.440 回答
5

这基本上是运气。显然,编译器碰巧将 new_link 粘贴到与返回值相同的位置。

于 2010-05-05T20:25:29.070 回答
5

为避免此问题,请使用:

-Wreturn-type

每当使用默认为 int 的返回类型定义函数时发出警告。还要警告在返回类型不是 void 的函数中任何没有返回值的返回语句(从函数体的末尾落下被认为返回没有值),以及在函数中带有表达式的返回语句返回类型是无效的。

-Werror=return-type将上述内容变为错误:

将指定的警告变为错误。附加了警告的说明符,例如 -Werror=switch 将 -Wswitch 控制的警告转换为错误。此开关采用否定形式,用于对特定警告否定 -Werror,例如 -Wno-error=switch 使 -Wswitch 警告不是错误,即使 -Werror 生效。您可以使用 -fdiagnostics-show-option 选项使用控制它的选项修改每个可控警告,以确定使用此选项的内容。

(来自GCC 警告选项

于 2010-05-05T23:55:59.693 回答
1

这是巧合。你不应该依赖那个。

于 2010-05-05T20:26:17.427 回答
1

最有可能的是它会产生一个很难找到的错误。我不确定我在哪里读到它,但我记得如果你忘记输入 return 语句,大多数编译器将默认返回 void。

这是一个简短的示例:

#include <iostream>

using namespace std;

int* getVal();

int main()
{
        int *v = getVal();
        cout << "Value is: " << *v << endl;
        return 0;
}

int* getVal()
{
        // return nothing here
}

对我来说,这也有效。但是,当我运行它时,我遇到了段错误。所以它真的是不确定的。仅仅因为它编译,并不意味着它会工作。

于 2010-05-05T20:40:07.553 回答
0

它之所以有效,是因为在 1940 年代创建 C 语言时,还没有return关键字。如果您查看 C43 MINSI 标准的“功能”部分,它对主题有这样的说法(除其他外):

16.4.3b For backwards compatibility the EAX register MUST be used to return
        the address of the first chunk of memory allocated by malloc.

</humour>

于 2010-05-05T21:21:01.243 回答
-1

可能是巧合:

函数返回值的空间是提前分配的。由于该值未初始化,因此它可能指向堆上与为结构分配的内存相同的空间。

于 2010-05-05T20:25:42.690 回答