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

发布于 2024-08-31 03:28:11 字数 499 浏览 8 评论 0原文

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

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。

Some old code that I just came across:

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

This was being called to build a linked list, however I noticed there is no statement:

return new_link;

Even without the return statement there, the list still got built properly. Why did this happen?

Edit: Platform: Mandriva 2009 64bit Linux 2.6.24.7-server GCC 4.2.3-6mnb1

Edit: Funny... this code also ran successfuly on about 5 different Linux installations, all different versions/flavors, as well as a Mac.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

櫻之舞 2024-09-07 03:28:11

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

这是不可移植的,而且实际执行起来非常危险,但也是让 C 编程变得如此有趣的小事情之一。

On 32-bit Windows, most of the time, the return value from a function is left in the EAX register. Similar setups are used in other OSes, though of course it's compiler-specific. This particular function presumably stored the new_link variable in that same location, so when you returned without a return, the variable in that location was treated as the return value by the caller.

This is non-portable and very dangerous to actually do, but is also one of the little things that makes programming in C so much fun.

国产ˉ祖宗 2024-09-07 03:28:11

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

Possible it just used the EAX register which normally stores the return value of the last function that was called. This is not good practice at all! The behavior for this sort of things is undefined.. But it is cool to see work ;-)

ι不睡觉的鱼゛ 2024-09-07 03:28:11

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

It's basically luck; apparently, the compiler happens to stick new_link into the same place it would stick a returned value.

淡莣 2024-09-07 03:28:11

要避免此问题,请使用:

-Wreturn-type

每当定义函数的返回类型默认为 int 时发出警告。还警告返回类型不为 void 的函数中任何没有返回值的 return 语句(脱离函数体的末尾被视为无值返回),以及函数中带有表达式的 return 语句,其返回值返回类型为空。

-Werror=return-type 将上面的内容变成错误:

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

(来自 GCC 警告选项

To avoid this problem, use:

-Wreturn-type:

Warn whenever a function is defined with a return-type that defaults to int. Also warn about any return statement with no return-value in a function whose return-type is not void (falling off the end of the function body is considered returning without a value), and about a return statement with an expression in a function whose return-type is void.

-Werror=return-type to turn the above into an error:

Make the specified warning into an error. The specifier for a warning is appended, for example -Werror=switch turns the warnings controlled by -Wswitch into errors. This switch takes a negative form, to be used to negate -Werror for specific warnings, for example -Wno-error=switch makes -Wswitch warnings not be errors, even when -Werror is in effect. You can use the -fdiagnostics-show-option option to have each controllable warning amended with the option which controls it, to determine what to use with this option.

(from GCCs warning options)

韵柒 2024-09-07 03:28:11

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

This works by coincidence. You shouldn't rely on that.

故乡的云 2024-09-07 03:28:11

最有可能的是,它会产生一个很难发现的错误。我不确定我在哪里读到它,但我记得如果你忘记放入 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
}

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

Most likely is that it'll produce a very hard to find bug. I'm unsure where I read it, but I recall that if you forget to put in a return statement, most compilers will default to return void.

Here's a short example:

#include <iostream>

using namespace std;

int* getVal();

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

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

For me, this works as well. However, when I run it, I get a segment fault. So it's really undefined. Just because it compiles, doesn't mean it'll work.

睫毛溺水了 2024-09-07 03:28:11

它之所以有效,是因为在 20 世纪 40 年代 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.

It works because in the 1940's when the C language was created, there was no return keyword. If you look in the "functions" section of the C43 MINSI standard, it has this to say on the subject (amongst other things):

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>

花辞树 2024-09-07 03:28:11

可能是巧合:

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

Probably a coincidence:

The space for the return value of the function is allocated ahead of time. Since that value is uninitialized, it could have pointed to the same space on the heap as the memory allocated for the struct.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文