C 为什么这个代码仍然有效?

C 为什么这个代码仍然有效?,c,undefined-behavior,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; 即使没有return语句,列表仍

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

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 64位Linux 2.6.24.7-server GCC 4.2.3-6mnb1


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

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


这是不可移植的,实际上非常危险,但也是让C语言编程如此有趣的一件小事。

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

可能是巧合:


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

这是巧合。您不应该依赖它。

可能它只是使用了EAX寄存器,该寄存器通常存储上次调用的函数的返回值。这根本不是好的做法!这类事情的行为尚未定义。。但看到工作很酷;-)

最有可能的是它会产生一个很难找到的bug。我不确定在哪里读到的,但我记得,如果您忘记输入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
}
#包括
使用名称空间std;
int*getVal();
int main()
{
int*v=getVal();

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


要避免此问题,请使用:

-Wreturn类型

每当使用默认为int的返回类型定义函数时发出警告。对于返回类型不是void的函数中没有返回值的任何return语句,也发出警告(从函数体末尾脱落视为没有返回值),以及关于返回类型为void的函数中带有表达式的返回语句

-Werror=返回类型
将上述内容转换为错误:

将指定的警告设置为错误。将附加警告的说明符,例如-Werror=开关将-Wswitch控制的警告设置为错误。此开关采用否定形式,用于否定特定警告的-Werror,例如-Wno error=开关使-Wswitch警告不为错误,即使-Werror为in效果。您可以使用-fddiagnostics show选项,用控制该选项的选项修改每个可控警告,以确定使用该选项的内容


(from)

除了愚蠢的运气之外,没有可能的答案,除非(或者甚至如果)你让我们知道是什么平台。这是使用静态代码检查器的一个很好的理由。有趣的是,它在这么多平台上运行。它只有一个局部变量,所以对于英特尔上的GCC来说,在EAX中存储一个局部变量肯定是相当标准的(或其64位等效项),并使用该寄存器返回值。因此,如果您在函数顶部声明一个要返回的变量,GCC是否足够聪明,可以从一开始就使用EAX?我认为这是一个很好的例子,例如
-Werror=return type
+1,另外定义调用类型的是ABI,而不是操作系统ntion.@peachykeen gcc会用
-Wall
警告这一点,但让我困惑的是为什么语言规范没有规定这是一个强制性的编译器错误。我想不出任何理由为什么允许这样做是个好主意。@Bryan:是的,打开
-Wreturn type
+1“让C编程变得如此有趣的小事情”@泰勒:编译器可以(非致命地)诊断这一点,因为任何事情都是这样。然而,在标准C的一般情况下,要求缺少return语句是一个错误是失败的,因为你不能指出永远不会返回的函数(例如调用abort或longjmp的函数):
int f(){int n=may_abort();if(n)返回n;do_longjmp();}
Uhh,不,返回值通常在寄存器中返回,或者,我想,在堆栈上返回。这不是一个神秘正确的随机指针。返回值没有分配。如上所述,返回值在寄存器中返回,如果它们合适,就像指针一样。@Whirl:在某些体系结构上,在堆栈上保留空间调用过程时返回值的堆栈。在其他情况下,结果在寄存器中返回。它不同。不,不是。没有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.