为什么在 C++ 中使用 main()不能内联吗?
我在阅读 C++ 常见问题解答时注意到一句话。
main() 不能内联。
这是为什么呢?
I was reading the C++ FAQs and I noticed one sentence.
main() cannot be inline.
Why is this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(17)
有许多基本原因。基本上,
main
是从运行时的基本初始化例程,并且只能从那里开始。
该代码(显然)是在不知道您的
main
的情况下编译的内联。现代编译器技术能够内联
模块边界,但它是一项高级功能,很多人并不支持
较旧的编译器。当然,内联的好处只是
当函数被频繁调用时出现;根据定义,
main
只会被调用一次,不多也不少。
There are a number of basic reasons. Basically,
main
is called fromthe basic initialization routine of the runtime, and only from there.
That code was (obviously) compiled without knowing that your
main
wasinlined. Modern compiler technology is capable of inlining across
module boundaries, but it's an advanced feature, not supported by many
older compilers. And of course, the benefits of inlining are only
present when a function is called very frequently; by definition,
main
will be called exactly once, no more, no less.
我看到标准是这么说的,但真正实际的答案很简单,就是声明添加到每个 C 和 C++ 程序的运行时必须调用可执行文件中的某个点。该函数应该有一个外部符号(以及运行时的地址),以便链接器可以在执行开始时找到要调用的函数。因此,您不能将其声明为内联,因为内联编译器不会为其生成外部符号。
I see the standard says so, but the real practical answer would be as simple as stating that the runtime added to every C and C++ program has to call to some point in the executable. That function should have an external symbol (and address when running) so that the linker can find it to be called at the beginning of execution. Hence you cannot declare it as
inline
, because inlined the compiler wouldn't generate an external symbol for it.由于 main() 函数开始执行,因此当代码编译为二进制时,所有内容都在
main()
本身中。所以你可以说,它已经内联了!是的,在 C++ 程序中使用内联是非法的,这更多的是关于语法!
Since its the main() function, which starts the execution, when the code gets compiled to binary, everything is in the
main()
itself. so you can say, it already inlined!And yes, its illegal to use inline for your C++ program, that's more about a syntax!
对于大多数编译器/架构的组合,源代码中的
main()
函数在最终的二进制文件中成为相当正常的函数。这只是因为它在这些架构上很方便,而不是因为标准规定必须如此。在内存受限的架构上,许多编译器(生成平面二进制文件(如 intex 十六进制格式)而不是动态链接器友好容器(如 elf 或 xcoff))会优化所有样板文件,因为它只会变得臃肿。有些架构根本不支持函数调用(在这些平台上只能使用 C++ 的有限子集。)
为了支持最广泛的此类架构和构建环境,标准选择保留
main( )
尽可能开放,以便编译器可以为最广泛的平台做正确的事情。这意味着该语言中提供的许多功能作为一个整体不能应用于应用程序本身的启动和关闭。如果您需要类似内联
main()
(或可重入,或任何奇特的功能)之类的东西,您当然可以将 main 函数称为其他名称:For most combinations of compiler/archetecture, the
main()
function in the source becomes a reasonably normal function in the final binary. This is only because it's convenient on those archetectures, not because the standard says it must be so.On memory constrained archetectures, many compilers, ones which produce a flat binary (like intex hex format) instead of a dynamic linker friendly container (like elf or xcoff), optimize all of the boilerplate away, since it would just be bloat. Some architectures don't support function calls at all (only a limited subset of C++ is possible on these platforms.)
In order to support the widest variety of such architectures and build environments, the standard elects keep the semantics of
main()
as open as possible, so that the compiler can do what's right for the widest variety of platforms. That means that many features available in the language as a whole cannot apply to the startup and shutdown of the application itself.If you need something like an inline
main()
(or reentrancy, or any fancy feature) you can of course call the main function something else:内联函数默认具有静态作用域。这意味着如果我们将 main() 声明为内联,它的范围将仅限于定义它的文件。然而,C 启动库(由编译器供应商提供)需要“main”作为全局符号。有一些编译器允许使用链接器标志修改入口点函数(例如 main)。
Inline functions are having static scope by-default. It means if we declare main() as inline, it's scope will be limited to the file where it is defined. Yet, the C start-up library (provided by compiler vendor) needs 'main' to be a global symbol. There are some compilers that allow to modify entry point function (e.g. main) using linker flags.
内联函数通常没有地址,因此没有可移植的方式来调用 main,main() 需要一个 init 代码可以跳转到的地址。内联函数意味着嵌入到调用函数中,如果 main 是内联的,则应该内联到程序的 init 代码中,这也是不可移植的。
inline functions don't usually have an address, so there is no portable way to call main, main() needs an address on which the init code can jump into. Inlined functions are meant to be stuck into the calling function, if main is inlined, it should be inlined into the init code of the program, which is not portable either.
操作系统将二进制数据加载到内存;寻找入口点(c/c++ 中的“主”符号);远跳转到入口点标签的地址。在程序未加载之前,操作系统对代码中的主函数一无所知。
operating system loads binary data to memory; looks for entry point (the 'main' symbol in c/c++); makes far jump to the addres of the entry point label. Operating system does not know anything about main function in your code until the program is not loaded.
在 C++ 中,在代码中调用 main 函数是不合法的,因此不可能内联它。
In C++ it is not legal to call the main function in your code, so there'd be no way it could ever be inlined.
因为标准是这样说的:
为什么这么说呢?因为它试图将
main
的实现尽可能多地留给个人......好吧,实现 ..尽可能,并且不希望通过要求来限制实现当内联可能没有实际好处时,它在这里是有效的。我在委员会的朋友证实了这一点:
顺便说一句,不要将
inline
提示关键字与实际的内联函数混淆。您可以将函数标记为内联,但它可能不是物理内联的。因此,即使
main
确实“无法内联”(严格来说,这是不,尽管内联main
会更合适)正如其他答案中所解释的那样,尴尬且毫无意义),理论上它仍然可以很好地支持inline
提示关键字。事实并非如此,正如 litb 的回答所述:这会使事情变得复杂,而没有真正的好处。
Because the standard says so:
And why does it say so? Because it's trying to leave as much about the implementation of
main
to the individual .. well, implementation .. as is possible, and doesn't want to limit implementations by requiring thatinline
be valid here when it arguably has no practical benefit.My friend on the committee confirmed this:
BTW, don't confuse the
inline
hint keyword with actually inlining functions. You can mark a functioninline
and it may not be physically inlined.So, even if it were true that
main
"cannot be inlined" (and strictly speaking it is not true, though inliningmain
would be rather awkward and pointless as explained in other answers), it could theoretically still support theinline
hint keyword just fine.It doesn't for the reason stated above, and in litb's answer: it would complicate matters for no real benefit.
C 运行时库需要找到此符号才能“知道”要运行哪个函数。
The C runtime library needs to find this symbol in order to "know" which function to run.
你不能直接调用 main() (在 C++ 中是禁止的),所以没有必要内联它。
You cannot directly call main() (it's forbidden in c++), so there is no point of inlining it.
通常
main()
是从系统init()
函数中调用的。因此,main()
需要只有一个定义。现在,如果我们可以
内联
main()
函数并将其包含在头文件中,那么对于每个翻译单元,main()< 都会有不同的定义/代码>。这是不允许的。您可以在
命名空间
中声明main()
并内联
它。但不是全局main()
。Usually
main()
is called from systemsinit()
function. Thus, it is needed that there can be exactly one definition formain()
.Now, if we can
inline
themain()
function and include in a header file then, for every translation unit there will be different definition formain()
. Which is not allowed. You can declaremain()
in anamespace
andinline
it. But not the globalmain()
.首先,您必须了解内联示例的工作原理
:
对于编译器来说,它看起来像:
看这个示例,您希望如何使 main 内联?该方法立即内联。
firstly you must understand how work function with inline
example:
will look like to the compiler as:
looking at this example, how do you want to make main inline? This method is inline immediately.
根据 @Tomalak Geret'kal 的回复,C++ 标准规定
main
函数不能内联。此响应讨论了如果标准中的限制被删除,内联main
函数的可能性。内联的定义
inline
关键字是对编译器在原位粘贴函数内容的建议。一个目的是消除调用函数(子例程)和返回函数时存在的开销。内联的一个重要情况是存在指向函数的指针的情况。在这种情况下,必须至少有一个该函数的静态副本。在这种情况下,链接器可以解析内联函数的“外部链接”,因为存在一个静态版本。
需要注意的是,编译器和链接器确定是否粘贴内容或调用函数的单个实例。
另外值得注意的是,未由程序员标记的函数也可能由编译器内联。
内联main函数
由于只允许调用一次
main
,因此它的如何链接取决于编译器。标准允许内联函数的单个实例。编译器可以将内联
函数转换为对单个实例的函数调用。因此编译器会忽略对main
函数的内联建议。编译器和链接器必须确保内联
main
函数仅存在一个实例。这就是棘手的部分,尤其是外部链接。确保一个实例的一种过程是留下翻译具有“主要”功能的信息,无论它是否内联。 注意:当调用内联函数时,编译器可以从符号表中删除该函数以进行外部链接,因为这个想法是该函数不会被外部函数调用。 >总结
技术上,没有什么可以阻止
main
函数被内联。用于将内联函数转换为单个实例以及识别函数的多个实例的机器已经存在。当存在指向内联函数的指针时,就会创建函数的单个实例,因此它有一个地址。该机器将满足运行时库对具有地址的main
的要求。对于main
函数的inline
,它将被忽略,但不应该有任何理由阻止这种语法(除了令人困惑的人)。毕竟,已经存在冗余的语法情况,例如将按值(副本)传递的参数声明为 const。“这只是我的意见,我可能是错的。” ——丹尼斯·米勒,喜剧演员。
The C++ standard says that the
main
function cannot be inlined, per @Tomalak Geret'kal's reply. This response discusses possibility of inlining of themain
function, were the restriction in the Standard removed.Definition of Inline
The
inline
keyword is a suggestion to the compiler to paste the contents of the function in-situ. One intent is to remove the overhead present in calling and returning from a function (subroutine).An important situation of inlining is the case where there is a pointer to the function. In this case, there must be at least one static copy of the function. In this case, the linker can resolve "external linkages" of the inlined function because there is one static version.
Important to note that the compiler and linker determine whether or not to paste the contents or calls a single instance of the function.
Also of note, functions that are not tagged by the programmer may also be inlined by the compiler.
Inlining the main function
Since there is only one invocation of
main
allowed, how it is linked is up to the compiler. Single instances of inline functions are allowed by the Standard. The compiler is allowed to convert aninlined
function into a function call to a single instance. So the compiler would ignore an inline suggestion for themain
function.The compiler and linker would have to insure that only one instance of the inlined
main
function exists. This where the tricky part comes in, especially with external linkage. One process for ensuring one instance is to leave information that a translation has a 'main' function whether or not it is inlined. Note: When a call to an inline function is made, the compiler is allowed to remove the function from the symbol tables for external linkage, since the idea is that the function won't be called by external functions.Summary
Technically, there is nothing preventing the
main
function from being inlined. The machinery already exists for converting inlined functions into single instances and for identifying multiple instances of a function. When there is a pointer to an inlined function, a single instance of a function is made, so it has an address. This machinery would satisfy the Run-Time Library requirements formain
having an address. In the case ofinline
for themain
function, it would be ignored but there should not be any reason to prevent this syntax (except confusing people). After all, there are already syntax cases that are redundant, such as declaring a parameter that is passed by value (copy) asconst
."That's just my opinion, I could be wrong." -- Dennis Miller, comedian.
其他人评论说,在机器代码级别内联
main
的调用没有意义。那是垃圾。它需要链接器的一些帮助(例如全局优化),或者每个应用程序重新编译一些运行时库,但这是完全可行的,这里没有技术问题。但是,
内联
的暗示效果(即调用最好是内联的)与仅调用一次且处于控制顶层的函数无关,如>主要是。
内联的唯一保证效果是允许在两个或多个翻译单元中(相同地)定义外部链接函数,即影响单一定义规则。
实际上,这允许将定义放置在头文件中,并且将其放置在头文件中实际上也是保证相同定义所必需的。
这对于
main
来说没有意义,因此main
没有理由内联
。Others have remarked that an invocation of
main
can not meaningfully be inlined at the machine code level. That's rubbish. It would require a bit of help from the linker (like global optimization) or else per-application recompilation of a bit of the runtime library, but it's quite doable, no technical problem here.However, the hinting effect of
inline
, that calls should preferably be inlined, is irrelevant for a function that is only called once and at the top level of control, asmain
is.The only guaranteed effect of
inline
is to allow an external linkage function to be defined (identically) in two or more translation units, i.e. affecting the One Definition Rule.As a practical matter this allows the definition to be placed in a header file, and placing it in a header file is a also practically necessary to guarantee identical definitions.
That does not make sense for
main
, so there is no reason formain
to beinline
.您只能定义一次
main
。因此,放置inline
不会有任何目的 -inline
仅对可以在程序中定义多次的函数有重要作用(所有定义都将被视为只有一个定义且所有定义必须相同)。因为
inline
函数可以在程序中定义多次,并且inline
还可以快速调用inline
标记的函数该标准尽可能要求在使用该标准的每个翻译单元中定义内联函数。因此,如果函数的定义是内联的,并且当前翻译单元中的代码未使用该函数,那么编译器通常会丢弃该函数的定义。对main
这样做是完全错误的,这表明inline
和main
的语义完全不兼容。请注意标题中的问题“为什么 C++ 中的 main() 不能内联?”您从标准中引用的声明涉及不同的事情。您问的是函数是否可以内联,通常理解为将被调用函数的代码完全或部分插入到调用函数中。仅将函数标记为内联并不意味着内联该函数。这完全是编译器的决定,当然,如果您从不调用 main(并且您不能这样做),那么就没有什么可以内联的。
You can only define
main
once. So puttinginline
would not serve any purpose -inline
only has a significant purpose on functions you can define multiple times in a program (all definitions will be treated as if there were only one definition and all definitions are required to be the same).Because
inline
functions can be defined multiple times in a program, andinline
also serves the purpose of making calls to aninline
-marked function as fast as possible, the Standard requiresinline
functions to be defined in every translation unit in which it is used. So compilers will usually throw away the definition of a function if it isinline
and the function wasn't used by the code in the current translation unit. To do that formain
would be entirely wrong, which goes to show thatinline
and the semanticsmain
has is entirely incompatible.Note that the question in your title "Why main() in C++ cannot be inlined?" and the statement you quote out of the Standard concern different things. You are asking whether the function can be inlined, which commonly is understood to insert the code of a called function completely or partially into the calling function. Just marking a function
inline
doesn't imply inlining that function at all. It's entirely the compiler's decision, and of course if you never callmain
(and you cannot do so) then there is nothing to be inlined.如果您静态链接到 CRT并且启用了一些链接时编译内联(如 MSVC 那样),则可能可以内联它。
但这确实没有意义。它将被调用一次,并且与 main 中第一行执行之前完成的所有其他操作相比,该函数调用开销几乎为零。
...
Aaand,这是强制符号在可执行文件中仅出现一次的简单方法。 :)
If you linked statically to the CRT and enabled some link-time compilation-inlining (like MSVC has) it might be possible to inline it.
But it doesn't really make sense. It will be called once and that function call-overhead is practically naught compared to everything else that is done before the first line in main executes.
...
Aaand, it is an easy way to force the symbol to appear only once in your executable. :)