为什么这段代码仍然有效?

时间:2010-05-05 20:20:10

标签: c undefined-behavior

我刚遇到的一些旧代码:

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;

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

编辑:平台:Mandriva 2009 64位Linux 2.6.24.7-服务器GCC 4.2.3-6mnb1

编辑:有趣......这段代码也在大约5种不同的Linux安装,所有不同的版本/口味以及Mac上成功运行。

8 个答案:

答案 0 :(得分:35)

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

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

答案 1 :(得分:7)

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

答案 2 :(得分:5)

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

答案 3 :(得分:5)

要避免此问题,请使用:

-Wreturn-type

  

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

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

  

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

(来自GCCs warning options

答案 4 :(得分:1)

这巧合。你不应该依赖它。

答案 5 :(得分:1)

最有可能的是它会产生很难找到的bug。我不确定我在哪里阅读它,但我记得如果你忘记输入一个返回语句,大多数编译器将默认返回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
}

对我来说,这也有效。但是,当我运行它时,我得到一个段错误。所以它确实是未定义的。仅仅因为它编译,并不意味着它会起作用。

答案 6 :(得分: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>

答案 7 :(得分:-1)

可能是巧合:

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