为什么C()中的main()不能被内联?

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

main()不能内联。

为什么是这样?

在C ++中,在你的代码中调用main函数是不合法的,所以它不可能被内联。

因为标准这样说:

[2003: 3.6.1/3] :程序中不能使用函数main(3.2)。 main的连接(3.5)是实现定义的。 声明main为内联或静态的程序是不合格的。 名称main不保留。 [例如:成员函数,类和枚举可以称为main,其他名称空间中的实体也可以称为main。 ]

为什么这样说呢? 因为它试图把main的实现留给个人..好吧, 实现 ..尽可能,并不想限制实现,要求inline在这里是有效的,当它可以说没有实际的好处。


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

inline main()本身没有任何理由。 [..]我可以有一个C ++解释器,可以调用内联main() 。 [..] [但是] inline / static main()被禁止以避免混淆。 我发现很难想象,这个基本原理可能是[本问答]已经提到的内容。


顺便说一句,不要混淆inline提示关键字与实际内联函数。 你可以标记一个函数inline ,它可能不是物理内联。

所以,即使main “不能内联”(严格来说不是真的,虽然内联main是相当尴尬和毫无意义的解释在其他答案),它在理论上仍然支持inline提示关键字就好。

这并不是出于上述原因,而是以莱特布的回答:这会使没有真正利益的事情复杂化。

C运行时库需要find这个符号以便“知道”运行哪个函数。

你不能直接调用main()(它在c ++中是被禁止的),所以没有任何内联的地方。

通常main()是从系统init()函数中调用的。 因此, main()只需要一个定义就可以

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

首先,您必须了解如何使用内联function

例:

  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; } 

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

C ++标准说,根据@Tomalak Geret'kal的回答, main函数不能被内联。 这个回应讨论了mainfunction内联的可能性,是否删除了标准中的限制。

内联的定义
inline关键字是build议编译器粘贴function的内容。 其中一个意图是从函数(子例程)中删除调用和返回中的开销。

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

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

另外值得注意的是, 没有被程序员标记的函数也可以被编译器内联。

内联主要function
由于只有一个main允许的调用,所以如何链接到编译器。 标准允许内联函数的单个实例。 编译器允许将inlined函数转换为对单个实例的函数调用。 所以编译器会忽略 main函数的内联build议。

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

概要
从技术上讲 ,没有任何东西可以阻止main函数被内联。 已经存在将内联函数转换为单个实例并识别函数的多个实例的机制。 当有一个指向内联函数的指针时,函数的一个实例被创build,所以它有一个地址。 该机器将满足运行时间库main地址的要求。 在main函数inline的情况下,它将被忽略,但不应该有任何理由来阻止这个语法(除了令人困惑的人)。 毕竟,已经存在冗余的语法情况,例如声明一个通过value(copy)传递的参数为const

“这只是我的看法,我可能是错的。” – 丹尼斯·米勒,喜剧演员

其他人已经指出,在机器代码层次上调用main并不是有意义的。 这是垃圾。 这需要来自链接器(比如全局优化)的一些帮助,或者每个应用程序重新编译一些运行时库,但这是相当可行的,没有技术问题。

然而, inline暗示效应,优先内联的调用,对于一个只被调用一次,并且在main控制的最高级别的function是无关紧要的。

inline的唯一保证作用是允许在两个或多个翻译单元中定义(同样)外部链接function,即影响“一个定义规则”。

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

这对main来说是没有意义的,所以没有理由将main放在inline

你只能定义一次main 。 因此, inline不会起任何作用 – inline仅对于可以在程序中多次定义的函数具有重要意义(所有定义将被视为只有一个定义,并且所有定义都必须相同)。

由于inline函数可以在程序中多次定义, inline也可以尽可能快地调用inline标记函数,所以标准要求inline函数在每个使用它的翻译单元中定义。 所以编译器通常会抛弃函数的定义,如果它是inline ,并且函数没有被当前编译单元中的代码使用。 要做到这一点main是完全错误的,这表明inlinemain的语义是完全不相容的。

请注意,标题中的问题“为什么main()in C ++不能被内联?” 你在标准中引用的声明涉及不同的事情。 您在询问函数是否可以内联,通常被理解为将调用函数的代码完全或部分地插入到调用函数中。 只是在线标记函数并不意味着内联该函数。 这完全是编译器的决定,当然,如果你永远不会调用main (并且你不能这样做),那么就没有内联。

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

但这并不合理。 它将被调用一次,并且与主执行的第一行之前完成的所有其他操作相比,函数调用开销实际上是零。

Aaand,这是一个简单的方法来强制符号在您的可执行文件中只出现一次。 🙂

有一些基本的原因。 基本上, main是从运行时的基本初始化例程调用的,并且只能从那里运行。 这段代码显然是在不知道你的main被内联的情况下编译的。 现代编译器技术能够跨越模块边界进行内联,但是这是一个高级特性,许多较老的编译器都不支持。 当然,内联的好处只有在函数被调用的时候才会出现, 根据定义, main被称为一次,不多,不less。

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

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

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

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

在内存受限的构架上,许多编译器(产生一个扁平的二进制文件(比如intexhex格式)而不是一个dynamic链接器友好的容器(比如elf或者xcoff))优化了所有的样板,因为它会膨胀。 一些体系结构根本不支持函数调用(在这些平台上只有C ++的有限子集)。

为了支持最广泛的这种架构和构build环境,标准select使main()的语义尽可能保持开放,以便编译器能够为最广泛的平台做正确的事情。 这意味着整个语言中可用的许多function并不适用于应用程序本身的启动和closures。

如果你需要内联的main() (或者重入,或者任何奇特的特性),你当然可以调用main函数:

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

内联函数默认具有静态作用域。 这意味着如果我们将main()声明为内联,那么它的作用域将被限制在它所定义的文件中。 然而,C启动库(由编译器供应商提供)需要“main”作为全局符号。 有一些编译器允许使用链接器标志来修改入口点函数(例如main)。

内联函数通常没有地址,所以没有可移植的方法来调用main,main()需要一个init代码可以跳转到的地址。 内联函数的目的是卡在调用函数中,如果main被内联,它应该被内联到程序的init代码中,这也是不可移植的。

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