如果主函数main()没有返回一个整数值会发生什么?

22

我知道在C编译器中,main()函数是由_start()函数调用的,其代码大致如下:

exit(main()); // return value of main is returned

main()的返回类型为voidfloat或其他类型时,_start()如何工作?


5
C和C++中main函数返回值的限制不同。 - fefe
@DanielFischer 对,但这与C++版本有何不同呢?在C+11中,它说“它应该具有类型int的返回类型”,位于3.6.1.2。 - Adrian Cornish
我听说Linux中的C运行时库,即crt1.o,它包含了你的_start(),是直接用汇编语言编写的,因此它没有类型系统。无论最终在预期的内存位置(或寄存器)中出现什么,都将被用作您的返回值... - Kerrek SB
@KerrekSB 这有关系吗 - 如果你只是说 c/c++,那么它必须在语言的每个实现上都能工作,无论是 Linux 服务器还是洗衣机。 - Adrian Cornish
1
Ok C99 5.1.22.1.1 表示“它应该定义为 int 类型的返回值”,c++14882 3.6.1.1 表示“它应该有一个 int 类型的返回值”,而 c++14882-2011 3.6.1.1 则表示“它应该有一个 int 类型的返回值”,因此 @fefe,它们在 C 和 C++ 之间似乎被明确定义为相同的。 - Adrian Cornish
显示剩余7条评论
8个回答

15

如果 main 没有返回 int,那么你的程序就是不合法的,行为是未定义的。任何事情都可能发生。你的程序可能会崩溃,或者它可能像什么都没有发生一样运行。

假设 main 返回了除 int 以外的其他类型,并且你的编译器和链接器允许生成该程序。但是调用者并不知道这一点。如果调用者期望返回的 int 值存储在 EAX(Intel)寄存器中,则它将读取该值以确定 main 的返回值。如果你有缺陷的 main 在那里存储了一个 float 值,那么它将被解释为一个 int。 (这并不意味着它会被截断。它意味着组成浮点值布局的位将代替组成 int 的位。)如果你有缺陷的 main 返回了 void,那么它没有在预期的寄存器中存储任何内容,因此调用者将得到先前在该寄存器中存储的任何值。

如果你的 main 返回某个类型,它期望将其存储在调用者没有为其保留内存的某个位置(例如一个大型结构体),那么它将覆盖其他东西,可能是程序干净关闭所必需的重要内容,导致你的程序崩溃。


3
如何将浮点数存储在EAX寄存器中? - Kerrek SB
1
@KerrekSB - 很好的问题。尽管在80486之前它相对常见。毕竟,浮点数只是32位的东西,您需要使用特殊规则来操作它。 - Omnifarious
1
浮点数是否以寄存器的形式返回,@Kerrek?我不知道。在我回答中描述的虚构场景中,“main”确实已经——以某种方式——将一个浮点数存储在调用者期望放置整数的位置。行为是未定义的,因此我可以描述任何奇怪的工作方式! - Rob Kennedy
1
@RobKennedy: 通常情况下,浮点数会返回到你的FPU中的浮点寄存器中(例如80387或现代MMX)。 - Kerrek SB
x86的cdeclST0中返回浮点数。新的SIMD寄存器仅在x64中使用,其中XMM0包含FP返回值。 - MSalters
在C语言中,如果实现允许,主函数main可以返回除int以外的其他类型--但是在托管型实现中很少有好的理由这样做。 - Keith Thompson

15

C标准从未提到过这个_start函数;我认为C++也没有。

在1999年之前的ISO标准中,如果执行到main()的结尾而没有执行return语句,或者执行了一个没有指定返回值的return语句,则“返回给主机环境的终止状态是未定义的”。实际上,我见过这样的程序返回1(失败)或一些任意的内存值,例如最后调用的函数的结果。

1999年的ISO C标准改变了这个规则:“到达终止main函数的}会返回值0”。这符合C++自至少1998年第一个ISO C++标准以来的规则。

(作为一种风格,即使不是强制要求,我喜欢在main的末尾明确使用return 0;。这与除main外的其他int函数一致,并且可以更好地移植到C99之前的编译器中。)

所有这些都假设main的返回类型是int。这是C标准特别支持的唯一类型(int main(void)int main(int argc, char *argv[])或等效形式),但(托管)实现可以支持其他实现定义的定义。C90标准没有明确涵盖这种情况,但C99说:“如果返回类型与int不兼容,则返回给主机环境的终止状态是未指定的。”

C++标准有些不同。对于托管实现,main必须被定义为返回int。参数是实现定义的,但C的两个标准形式都必须得到支持。

对于用C或C++进行托管实现,我不知道有什么好的理由来定义 main 的返回类型除了int。只需使用这两个标准定义之一,问题就不会出现。

对于“自由实现”,“程序启动时调用的函数的名称和类型为实现定义”。因此入口点可能合法地返回void或其他内容,并且它甚至可能不被称为main。请注意,“自由实现”是指“在其中可以执行C程序而无需任何操作系统支持”的典型嵌入式系统。


根据这个定义,系统内核开发不也算是“独立实现”吗?当然,如果操作系统内核到达入口点函数的末尾,那就有问题了... - user
@MichaelKjörling:很可能。实际上,如果实现者说它是“独立的”(并符合更弱的独立实现要求),那么它就是“独立的”。 - Keith Thompson
@KeithThompson:在 C99 之前,如果程序执行完 main() 函数时既没有执行 return 语句,也没有指定返回值的 return 语句,那么“返回给操作系统的终止状态是未指定的”,而不是 undefined。 - Destructor
@PravasiMeet: 请问你引用这句话的出处是什么?C90标准,5.1.2.2.3章节中写道:“如果主函数执行了一个没有指定返回值的return语句,则返回给宿主环境的终止状态是未定义的。”(强调部分)当然,“未指定的”可能更合理一些。 - Keith Thompson

8

该函数将返回一个实现定义的值。例如,在C++中,main隐式地返回0。在void main的情况下,则会由_start简单地返回此值。但是,几乎没有任何实现允许任意的返回类型-退出进程时都必须使用整数值,这已经被嵌入到操作系统中了。


7

在C++中,如果从main()函数中返回除了int以外的任何类型,那么将会出现编译错误:

error: ‘::main’ must return ‘int’

在C语言中,这是一个警告,您将会得到一个被重新解释为int的浮点数:例如,2.1F将被重新解释为224。

1
FYI,gcc 默认情况下不会发出任何警告。 - shinkou
@shinkou 我刚试了一下gcc - 它给了我 warning: return type of ‘main’ is not ‘int’ - Sergey Kalinichenko
顺便说一下,我在gcc版本4.5.2(64位)上进行了测试。 - shinkou

2

C的标准实现要求main函数返回一个int类型,这是由C标准定义的。如果返回的不是int(或与int兼容的类型),那么通常会导致未定义的行为,也就是说无法确定会发生什么。

然而,也有非标准的C实现。例如,Plan 9操作系统使用的是void main()这里有他们实用程序源代码的列表。Plan 9的C代码与K&R、ANSI、C99或C11有很大的区别。这里有一个链接,解释了Plan 9如何使用C语言。


2
如果main的返回类型不是int,那么返回值就是实现定义的。简而言之,实现允许main的返回类型与int不同,但已知的实现都只支持int类型。理想情况下,您需要参考平台和编译器的文档,以查看它定义的确切行为,因为标准允许其具有灵活性。
参考资料:
C++03标准:
3.6.1主函数[basic.start.main]
一个实现不应该预定义main函数。这个函数不应该被重载。它应该有一个返回类型为int的返回类型,但其类型是实现定义的。所有实现都应该允许以下两种定义的main:
int main() { /* ... */ }

int main(int argc,char * argv []) { /* ... */ }
.....

这难道不意味着它必须具有int返回类型,但除此之外,只要实现允许没有参数和argc/argv(即返回类型int,但main函数的类型是实现定义的),则可以采用任何参数。我在等待咖啡沉淀,所以可能我说的不清楚... - dreamlax

0

假设我们正在使用Visual Studio 2012。

对于C++程序,Visual Studio允许指定void作为返回类型,尽管这在C++标准中是被禁止的。根据标准,在托管实现中,main()必须返回一个int

对于C程序,main()可以返回任何返回类型,但返回除int以外的其他内容会导致未指定的行为。例如,在Visual Studio 2012下,从double main()返回0.0会导致在调试器中运行程序时返回值为0xcccccccc(请参见在Visual Studio C++中,内存分配表示是什么?)。


0
C标准不允许您返回除int 或void之外的任何其他值 - c编译器会特别检查main的签名以确保其兼容。

3
C99(以及我所见过的C11草案)没有提到“void”。只有一个返回类型为“int”的明确提及“它应该被定义为返回类型为‘int’”,但不幸的是,这句话以“或者以某种其他实现定义的方式”结束(5.1.2.2.1)。 - Daniel Fischer
因为void return 5.1.2.2.1的评论被downvote了,但是该条款并没有提到void returns。 - Adrian Cornish
@DanielFischer:我认为“以某种其他实现定义的方式”是指参数及其类型,而不是返回类型。 - dreamlax
@dreamlax 我相信这是意图。但就我对英语的了解而言,它可以被理解为允许任意实现定义的类型。我曾经看到过这样的争论。不幸的是,尚未完全明确 int 是唯一允许的返回类型(对于托管实现)。 - Daniel Fischer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接