为啥 C++ 中的 main() 不能内联?

Posted

技术标签:

【中文标题】为啥 C++ 中的 main() 不能内联?【英文标题】:Why main() in C++ cannot be inlined?为什么 C++ 中的 main() 不能内联? 【发布时间】:2011-10-22 07:20:38 【问题描述】:

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

main() 不能内联。

这是为什么?

【问题讨论】:

对我来说更有趣的问题:为什么有人想尝试内联它? 在 os 内核代码中内联你的 main ? :) 这很愚蠢,对吧?内联是将方法的内容直接放入调用代码的地方,而不是单独的方法。这意味着您需要重新编译您的操作系统才能将您的main 函数编译到其中。所以答案是,因为你不能重新编译你的操作系统? @Kieren:这就是为什么你不想在物理上内联函数。这与函数不应标记为inline的原因并不完全相同(请记住,这只是一个提示!)。 把它放在 C++ 常见问题解答中对我来说似乎有点愚蠢,因为你为什么要这样做。这就像你看到一个没有任何意义的情况的警告标签。 【参考方案1】:

在 C++ 中,在代码中调用 main 函数是不合法的,因此不可能内联它。

【讨论】:

原因,想想吧。 @iammilind: *static_cast<int*>(0) = 10 也可以编译,但这并不意味着它是正确的......就像任何 ODR 违规以及许多其他事情一样......事实上compiles 并不意味着它是一个合法的程序。 @iammilind:“它编译”的语句需要上下文。因为标准当然不要求编译,实际上并不是在所有情况下都能编译。 sigh 对于任何想知道的人,op 在评论中询问“出于某种原因”,然后我评论回答他,但他删除了他的。不酷,操作。 @sepp2k:看我的回答。但简而言之,机器代码内联与问题无关,但从技术上讲,它可以以两种不同的方式内联在运行时库的调用中。然而,它没有完成,因为没有优势。 :-)【参考方案2】:

你不能直接调用main()(在c++中是禁止的),所以没有内联的意义。

【讨论】:

“没有意义”不是完全取缔某事的充分理由。还有[稍微]比这更多。【参考方案3】:

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

【讨论】:

这是否意味着链接器找不到其他内联函数的符号? @Thomas Matthews:取决于链接器的智能程度。一般来说,不,链接器不知道内联函数;他们有内部联系。更现代的链接器更聪明一些,因为它们会尝试对整个程序进行优化,这是完全不同的球赛。 :) 我还要说,在 C 运行时(通常)有一个显式的 callmain() 函数,它几乎总是动态链接。所以它不可能在典型情况下工作。【参考方案4】:

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

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

【讨论】:

没有inline 也可以这样做。 @Tomalak,那么会导致多重定义错误。不是吗? 如果每个定义都有内部链接,则不会。 (注意,这也是static int main() 格式错误的原因:D) @Tomlak,是的。 static int main() 等价于 namespace int main() 。我已经在答案中提到了。 也可以内联一个函数并且只有一个实例。编译器和链接器可以识别main 函数的多个实例,那么您的观点是什么?【参考方案5】:

首先你必须了解内联是如何工作的

示例:

 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;
 

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

【讨论】:

@the_drow:我希望 nirmus 会看到这一点,并且必须自己考虑修复!不过谢谢! 那么处理只有一次调用的inlined 函数和只有一次调用的main 函数有什么区别? This method is inline immediately. 不正确。 inline 只是一个提示。它不执行函数内联。【参考方案6】:

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

但这并没有真正的意义。它将被调用一次,与 main 中第一行执行之前完成的所有其他操作相比,该函数调用开销几乎为零。

...

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

【讨论】:

【参考方案7】:

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

【讨论】:

在大多数或可能所有系统上,负责调用main 的不是操作系统。相反,操作系统调用程序的机器代码级别入口点。对于 C 和 C++,入口点通常是运行时库中的一个函数,它依次执行各种初始化任务,然后调用 main,最后清理(例如调用已安装的退出处理程序)并退出。【参考方案8】:

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

然而,inlinehinting 效果(最好是内联调用)与只在顶层控制层调用一次的函数无关,因为 main 是.

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

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

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

【讨论】:

main 没有理由成为inline”是令人信服的,但不能直接解释为什么它被制作成不能标记为@ 987654330@. 我在回答中解释了同样的事情。不过,你的更精致。 嗯,我认为,该标准并没有竭尽全力支持没人会使用的东西。但我认为另外,main 的描述并不完美。例如,我一直认为并且仍然认为“在 main 的第一个语句之后”位是根本错误的。但我从未在任何地方看到它讨论过。或许,只是我对英语的理解不够…… @anonymous downvoters:请解释您拒绝投票的原因,以便其他人可以从您的见解中受益(呵呵)。 @Alf:与此类似,除非有正当理由,否则该标准不会特意禁止某些事情。 :)【参考方案9】:

因为标准是这样说的:

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

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


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

inline main() 本身没有理由不起作用。 [..] 我可以有一个可以调用内联main() 的 C++ 解释器。 [..] [但是] inline/static main() 是被禁止的,以避免混淆。我发现很难想象其基本原理会是[此问答]中已经说过的内容之外的任何其他内容。


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

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

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

【讨论】:

+1 用于引用标准。但是,这可能无法完全回答 O.P. 的问题;到目前为止,除了你的帖子,我还没有看到任何合理的答案。 @Thomas:我给出的基本原理与其他答案中的基本原理几乎相同,只是关于为什么可能没有实际好处的细节较少。 :) re“不想通过要求内联有效来限制实现”,支持inlinemain 是微不足道的,因为它可以被忽略,因此不限制任何实现,因此,该标准被禁止的可能原因并不成立。对不起。但我没有比我的回答提供更多的东西了,拥有inline 是没有意义的(我认为我们同意这一点)。 @Cheersandhth.-Alf:这有点暗示你可以在多个 TU 中定义 main,如果定义在词汇上都是相同的(除了其他限制之外),这没什么意义值得禁止。 @meet:为什么不应该这样?与用户定义的其他函数不同,main 具有必须与实现的运行时和主机操作系统交互的含义(因为它是程序入口点),因此人们委员会要求太多关于它。回想一下,其他函数的链接是用户定义的,所以事实上,标准 is 在这里稍微限制了main,说“听你的编译器供应商的意见,因为 他们选择这个不是你”。 :)【参考方案10】:

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

是的,在 C++ 程序中使用内联是非法的,这更多的是关于语法!

【讨论】:

【参考方案11】:

根据@Tomalak Geret'kal 的回复,C++ 标准规定不能内联main 函数。此响应讨论了 main 函数内联的可能性,如果标准中的限制被删除。

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

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

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

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

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

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

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

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

【讨论】:

【参考方案12】:

您只能定义一次main。因此,放置inline 不会有任何用途 - inline 仅对您可以在程序中多次定义的函数有重要用途(所有定义都将被视为只有一个定义,并且所有定义都必须是一样)。

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

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

【讨论】:

标准中的术语有点别扭,但内联函数确实可以被多次定义,但所有定义必须相同,并且代码的行为必须像只定义了一次。 (必须在每个使用它的翻译单元中定义内联函数这一事实有点问题。唯一将使用它的翻译单元是您没有编写的,已经与您的系统一起编译的翻译单元。) @James:重新插入括号,是的,但是允许实现做任何它想要的魔法。 @Alf 同意,只要保持可观察的行为即可。但是标准不需要任何这样的魔法。允许 main 内联将需要它。从历史上看,C++ 不喜欢需要魔法。 (但那是在模板之前。)【参考方案13】:

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

【讨论】:

【参考方案14】:

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

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

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

如果您需要类似内联 main() 的东西(或可重入性,或任何花哨的功能),您当然可以调用 main 函数:

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

【讨论】:

【参考方案15】:

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

【讨论】:

标记函数inline不一定会导致函数内联。 inline 函数具有外部链接,除非它是明确声明 static 的命名空间范围函数。【参考方案16】:

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

【讨论】:

没有可移植的方式在 C++ 程序中调用 main,即使它不是 inline【参考方案17】:

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

【讨论】:

以上是关于为啥 C++ 中的 main() 不能内联?的主要内容,如果未能解决你的问题,请参考以下文章

为啥c#中的main方法总是放在类中而不是c++中

为啥我不能在我的类中内联函数? [复制]

为啥我不能内联调用 res.json?

为啥我的 <legend> 元素不能内联显示?

为啥内联函数的效率低于内置函数?

为啥我不能将整数向量推入 C++ 中的二维整数向量?