为什么C ++中的main()无法内联?

时间:2011-08-08 10:56:52

标签: c++ inline main

我正在阅读C ++常见问题解答,我注意到了一句话。

  

main()不能内联。

为什么会这样?

17 个答案:

答案 0 :(得分:104)

在C ++中,在代码中调用main函数是不合法的,因此无法将其内联。

答案 1 :(得分:66)

因为标准是这样说的:

  

[2003: 3.6.1/3]:   函数main不得在程序中使用(3.2)。该   main的链接(3.5)是实现定义的。 一个程序   声明主要是内联或静态是不正确的。主要名称是   没有其他保留。 [示例:成员函数,类和   枚举可以称为main,其他名称空间中的实体也可以。   ]

为什么会这样说?因为它试图尽可能多地将main的实现留给个人......好吧,实现 ..尽可能,并且不希望通过要求{{来限制实现。 1}}在它可以说没有实际好处的时候有效。


委员会的朋友证实了这一点:

  

inline inline本身没有理由不起作用。 [..]我可以有一个可以调用内联main()的C ++解释器。 [..] [但] main() / inline static是被禁止的,以避免混淆。我发现很难想象这个理由是[此Q& A]中已经说过的内容。


顺便说一句,不要将main()提示关键字与实际内联函数混淆。您可以标记函数inline,但它可能没有实际内联。

所以,即使确实inline“无法内联”(并且严格来说它是真的,但内联main会相当尴尬和毫无意义正如其他答案中所解释的那样,它理论上仍然可以支持main提示关键字。

这不是出于上述原因,并且在litb的回答中:它会使事情变得复杂而没有真正的好处。

答案 2 :(得分:27)

C运行时库需要找到此符号才能“知道”要运行的函数。

答案 3 :(得分:17)

你不能直接调用main()(它在c ++中被禁止),所以没有内联点。

答案 4 :(得分:14)

通常从系统main()函数调用init()。因此,main()需要完全一个定义

现在,如果我们可以inline main()函数并包含在头文件中,那么对于每个翻译单元,main()都会有不同的定义。这是不允许的。您可以在main()namespace中声明inline。但不是全球main()

答案 5 :(得分:10)

首先,您必须了解内联

的工作方式

示例:

 inline void f() {
     int a  = 3;
     a += 3;
     cout << a;
 }

 int main() {
      f();
      return 0;
 }

看起来像编译器:

 int main() {
        int a  = 3;
        a += 3;
        cout << a;
        return 0;
 }

看看这个例子,你想如何制作主内联?此方法立即内联。

答案 6 :(得分:7)

其他人已经注意到main的调用无法在机器代码级别进行有意义的内联。那是垃圾。它需要来自链接器的一些帮助(如全局优化)或者每个应用程序重新编译一些运行时库,但这是非常可行的,这里没有技术问题。

然而,inline暗示效果,最好是内联调用,与仅调用一次并处于最高控制级别的函数无关,如{{ 1}}是。

main的唯一保证效果是允许在两个或多个翻译单元中定义(相同)外部链接功能,即影响单一定义规则。

实际上,这允许将定义放在头文件中,并将其放在头文件中也是保证相同定义的实际必要条件。

这对inline没有意义,因此main没有理由main

答案 7 :(得分:7)

根据@Tomalak Geret'kal的回复,C ++标准说main函数无法内联。该回复讨论了main函数内联的可能性,是否删除了标准中的限制。

内联的定义
inline关键字是编译器的建议,用于在原位粘贴函数的内容。一个目的是消除调用和返回函数(子例程)中存在的开销。

内联的一个重要情况是指向函数的指针。在这种情况下,必须至少有一个该函数的静态副本。在这种情况下,链接器可以解析内联函数的“外部链接”,因为有一个静态版本。

重要的是要注意编译器和链接器确定是否粘贴内容或调用函数的单个实例。

同样值得注意的是,编程器也可以内联未被编程器标记的函数。

内联主要功能
由于只有一个main允许的调用,如何它被链接到编译器。标准允许单个内联函数实例。允许编译器将inlined函数转换为对单个实例的函数调用。因此编译器会忽略 main函数的内联建议。

编译器和链接器必须确保只存在一个内联main函数的实例。这是棘手的部分,特别是外部链接。确保一个实例的一个过程是保留翻译具有“主要”功能的信息,无论其是否被内联。 注意:当调用内联函数时,允许编译器从符号表中删除函数以进行外部链接,因为这个想法是外部函数不会调用该函数。

<强>摘要
技术上,没有任何东西阻止main函数被内联。 机器已经存在,用于将内联函数转换为单个实例并用于标识函数的多个实例。当存在指向内联函数的指针时,会生成一个函数的单个实例,因此它具有一个地址。此机制将满足main具有地址的运行时库要求。对于inline函数的main,它将被忽略,但不应该有任何理由阻止这种语法(除了让人困惑)。毕竟,已经存在多余的语法案例,例如将值(副本)传递的参数声明为const

“这只是我的意见,我可能是错的。” - 喜剧演员丹尼斯米勒。

答案 8 :(得分:6)

您只能定义main一次。因此,将inline放在任何目的上都不会 - inline对于您可以在程序中多次定义的函数具有重要意义(所有定义都将被视为只有一个定义且所有定义都是必须是相同的。)

因为inline函数可以在程序中多次定义,inline也可以用来尽可能快地调用inline标记函数,标准要求inline函数将在使用它的每个翻译单元中定义。因此编译器通常会抛弃函数的定义(如果它是inline并且当前转换单元中的代码没有使用该函数)。为main执行此操作将完全错误,这表明inline和语义main完全不兼容。

请注意,标题中的问题“为什么C ++中的main()无法内联?”你从标准中引用的陈述涉及不同的事情。您正在询问是否可以内联函数,这通常被理解为将被调用函数的代码完全或部分地插入到调用函数中。仅标记函数inline并不意味着完全勾勒该函数。这完全是编译器的决定,当然如果你从不调用main(你不能这样做),那么就没有任何内联。

答案 9 :(得分:3)

如果您静态链接到CRT 并且启用了一些链接时编译内联(如MSVC),则可以内联它。

但它确实没有意义。它将被称为一次,与主执行中第一行之前完成的所有其他操作相比,函数调用开销实际上是零。

...

Aaand,这是一种强制符号在可执行文件中只出现一次的简单方法。 :)

答案 10 :(得分:2)

有许多基本原因。基本上,main被调用 运行时的基本初始化例程,并且仅从那里开始。 (显然)编译的代码不知道你的main是什么 内联。现代编译器技术能够内联 模块边界,但它是一个高级功能,许多人不支持 较旧的编译器。当然,内联的好处只是 当一个函数被频繁调用时出现;根据定义,main 将被称为恰好一次,不多也不少。

答案 11 :(得分:2)

我看到标准是这样说的,但真正的实际答案就像声明添加到每个C和C ++程序的运行时必须调用可执行文件中的某个点一样简单。该函数应该有一个外部符号(和运行时的地址),以便链接器可以在执行开始时找到它。因此,您不能将其声明为inline,因为内联编译器不会为其生成外部符号。

答案 12 :(得分:1)

由于main()函数开始执行,当代码被编译为二进制时,所有内容都在main()本身。所以你可以说,它已经内联了!

是的,非法使用内联你的C ++程序,更多的是语法!

答案 13 :(得分:1)

对于编译器/ archetecture的大多数组合,源中的main()函数在最终二进制文件中成为一个相当正常的函数。这只是因为它在这些建筑上很方便,而不是因为标准说它必须如此。

在内存受限的archetectures上,许多编译器,生成平面二进制文件(如intex十六进制格式)而不是动态链接器友好容器(如elf或xcoff),优化了所有的样板,因为它只是膨胀。有些架构根本不支持函数调用(在这些平台上只能使用有限的C ++子集。)

为了支持最广泛的此类体系结构和构建环境,标准选项使main()的语义尽可能保持开放,以便编译器可以为最广泛的平台做正确的事情。这意味着整个语言中可用的许多功能都不适用于应用程序本身的启动和关闭。

如果您需要内联main()(或重入,或任何奇特的功能),您当然可以将主要功能称为其他内容:

inline int myMain(int argc, char **argv) { /* whatever */ }
int main(int argc, char **argv) { return myMain(argc, argv); }

答案 14 :(得分:1)

默认情况下,内联函数具有静态范围。这意味着如果我们将main()声明为内联,它的范围将仅限于定义它的文件。然而,C启动库(由编译器供应商提供)需要'main'作为全局符号。有些编译器允许使用链接器标志修改入口点函数(例如main)。

答案 15 :(得分:0)

内联函数通常没有地址,因此没有可移植的方法来调用main,main()需要一个init代码可以跳转到的地址。内联函数意味着插入到调用函数中,如果main是内联的,它应该内联到程序的init代码中,这也是不可移植的。

答案 16 :(得分:-2)

操作系统将二进制数据加载到内存中;寻找入口点(c / c ++中的'main'符号);远远跳转到入口点标签的地址。在未加载程序之前,操作系统对代码中的主要功能一无所知。