main()真的是C ++程序的开始吗?


131

C ++ Standard的$ 3.6.1 / 1部分内容如下:

程序应包含一个称为main的全局函数,这是程序的指定开始

现在考虑这段代码,

int square(int i) { return i*i; }
int user_main()
{ 
    for ( int i = 0 ; i < 10 ; ++i )
           std::cout << square(i) << endl;
    return 0;
}
int main_ret= user_main();
int main() 
{
        return main_ret;
}

此示例代码执行了我打算执行的操作,即进入main()应该作为程序“开始” 的函数之前,打印从0到9的整数平方。

我还使用-pedantic选项GCC 4.5.0 对其进行了编译。它没有错误,甚至没有警告!

所以我的问题是

这段代码真的符合标准吗?

如果符合标准,那么它不会使标准中的内容无效吗?main()不是该程序的启动!user_main()在之前执行main()

我知道要初始化全局变量main_retuse_main()首先执行,但这完全不同。关键是,它确实会使标准中引用的语句$ 3.6.1 / 1无效,而main()不是程序的开始;实际上,程序已结束


编辑:

您如何定义“开始”一词?

归结为短语“程序启动”的定义。那么您如何定义它呢?

Answers:


85

不,C ++在main调用之前做了很多事情来“设置环境”。但是,主要是C ++程序“用户指定”部分的正式启动。

有些环境设置是不可控制的(例如设置std :: cout的初始代码;但是,某些环境是可控制的,例如静态全局块(用于初始化静态全局变量)。请注意,因为您没有完整的在main之前先进行控制,则无法完全控制静态块的初始化顺序。

主代码之后,从概念上讲,您既可以指定要执行的指令,也可以指定执行顺序,从某种意义上说,您的代码将完全由程序控制。多线程可以重新排列代码的执行顺序;但是,您仍然可以使用C ++进行控制,因为您指定要让代码段(可能)无序执行。


9
为此 +1 ”(请注意,由于您在main之前没有完全控制权,因此您无法完全控制静态块的初始化顺序。main之后,您的代码在概念上完全“控制”了在某种意义上说,您既可以指定要执行的指令,也可以指定执行顺序”。这也让我记住这答案,因为公认的答案 ......我觉得这些都是非常重要的观点,即足以证明main()作为“启动程序”
纳瓦兹

13
@Nawaz:请注意,除了无法完全控制初始化顺序之外,您也无法控制初始化错误:您无法在全局范围内捕获异常。
安德烈·卡伦

@Nawaz:什么是静态全局块?您能用简单的例子解释一下吗?谢谢
破坏者

@meet:在命名空间级别声明的对象具有static存储期限,因此,可以按任何顺序初始化属于不同翻译单元的这些对象(因为该顺序由标准指定)。我不确定这是否能回答您的问题,尽管我可以在本主题的背景下这样说。
纳瓦兹

88

您读错了句子。

程序应包含一个称为main的全局功能,这是程序的指定启动位置。

该标准是为标准其余部分的目的定义“开始”一词。并不是说没有代码在main被调用之前执行。它说程序的开始被认为是在功能上main

您的程序符合要求。在主程序启动之前,您的程序尚未“启动”。根据标准中“启动”的定义,在程序“启动”之前调用构造函数,但这无关紧要。代码很多之前执行的main曾经被称为在每一个程序,不只是这个例子。

为了便于讨论,您的构造函数代码在程序的“开始”之前执行,并且完全符合该标准。


3
抱歉,但是我不同意您对该条款的解释。
Lightness Races in Orbit

我认为Adam Davis是正确的,“ main”更像是某种编码限制。
laike9m 2013年

@LightnessRacesinOrbit我从来没有跟进过,但是对我来说,这个句子可以从逻辑上简化为“一个名为main的全局函数是程序的指定启动位置”(加了强调)。您对那句话的解释是什么?
亚当·戴维斯

1
@AdamDavis:我不记得我的担心是什么。我想不起来了。
Lightness Races in Orbit

23

除非有主程序,否则您的程序将不会链接,因此不会运行。但是main()不会导致程序开始执行,因为文件级别的对象具有预先运行的构造函数,并且有可能在到达main()之前编写一个运行其生命周期的整个程序,并使main本身具有一个空的身体。

实际上,要强制执行此操作,您必须在main及其构造函数之前构造一个对象,以调用程序的所有流程。

看这个:

class Foo
{
public:
   Foo();

 // other stuff
};

Foo foo;

int main()
{
}

您程序的流程将有效地源自 Foo::Foo()


13
+1。但是请注意,如果在不同的转换单元中有多个全局对象,这将使您迅速陷入困境,因为调用构造函数的顺序是不确定的。您可以摆脱单例和延迟初始化,但是在多线程环境中,事情很快就会变得很丑陋。一句话,不要在真实代码中执行此操作。
Alexandre C.

3
虽然您可能应该在代码中给main()一个适当的主体并允许其运行执行,但是启动之外的对象的概念是许多LD_PRELOAD库所基于的。
CashCow 2011年

2
@Alex:标准说未定义,但实际上,链接顺序(通常取决于编译器)控制初始化顺序。
ThomasMcLeod

1
@Thomas:我当然不会甚至试图依靠它。我当然也不会尝试手动控制构建系统。
Alexandre C.

1
@Alex:不再那么重要了,但是在过去,我们将使用链接顺序来控制构建映像,以减少物理内存分页。即使不影响初始化程序的顺序,它也可能有其他方面的原因,例如启动性能比较测试。
ThomasMcLeod

15

您也将问题标记为“ C”,然后严格来讲C,按照ISO C99标准的6.7.8节“初始化”,初始化将失败。

在这种情况下,最相关的似乎是约束4:

具有静态存储持续时间的对象的初始化程序中的所有表达式应为常量表达式或字符串文字。

因此,您的问题的答案是该代码不符合C标准。

如果您只对C ++标准感兴趣,则可能要删除“ C”标记。


4
@ Remo.D您能否告诉我们该部分的内容。并非所有人都有C标准:)。
UmmaGumma 2011年

2
既然您是如此挑剔:las,自1989年以来ANSI C就已经过时了。ISOC90或C99是需要引用的相关标准。
伦丁

@Lundin:没人挑剔:)我正在阅读ISO C99,但我非常有信心它也适用于C90。
Remo.D 2011年

@Ashot。您是对的,在此添加了我认为最相关的句子。
Remo.D 2011年

3
@Remo:+1,用于提供无效的C信息;我不知道。看到人们是这样学习的,有时是按计划的,有时是偶然的!
Nawaz

10

整体上,第3.6节非常清楚main动态初始化之间的交互。“程序的指定启动”在其他任何地方都没有使用,而只是描述的一般意图main()。以一种规范性的方式来解释一个短语与标准中更详细,更清晰的要求相抵触没有任何意义。


9

为了符合标准,编译器通常必须在main()之前添加代码。因为该标准指定必须在执行程序之前对全局变量/静态变量进行初始化。如前所述,位于文件作用域(全局)的对象的构造函数也是如此。

因此,最初的问题与C有关,因为在C程序中,您仍然需要全局/静态初始化才能启动程序。

这些标准假定这些变量是通过“魔术”初始化的,因为它们没有说明在程序初始化之前应如何设置它们。我认为他们认为这超出了编程语言标准的范围。

编辑:参见例如ISO 9899:1999 5.1.2:

在程序启动之前,所有具有静态存储持续时间的对象都应初始化(设置为其初始值)。否则未指定这种初始化的方式和时间。

这种“魔术”是如何实现的,其理论可以追溯到C的诞生,当时C是一种编程语言,仅用于基于RAM的计算机上的UNIX OS。从理论上讲,该程序将能够在将程序本身上载到RAM的同时,将所有预初始化的数据从可执行文件加载到RAM中。

从那时起,计算机和操作系统得到了发展,并且C的使用范围比最初预期的要广泛得多。现代PC操作系统具有虚拟地址等,并且所有嵌入式系统都从ROM(而非RAM)执行代码。因此,在许多情况下,无法“自动”设置RAM。

而且,该标准太抽象了,以至于不了解有关堆栈和进程内存等的任何信息。在启动程序之前,也必须完成这些操作。

因此,几乎每个C / C ++程序都有一些init /“ copy-down”代码,这些代码在调用main之前执行,以便符合标准的初始化规则。

例如,嵌入式系统通常具有一个称为“非ISO兼容启动”的选项,出于性能方面的考虑,整个初始化阶段将被跳过,然后代码实际上直接从main开始。但是,此类系统不符合标准,因为您不能依赖全局/静态变量的初始值。


4

您的“程序”仅从全局变量返回一个值。其他所有内容都是初始化代码。因此,标准成立了-您只有一个非常简单的程序和更复杂的初始化。



2

好像是英语语义学的bble测。OP首先将其代码块称为“代码”,然后称为“程序”。用户编写代码,然后由编译器编写程序。


1

在初始化所有全局变量后调用main。

标准未指定的是所有模块和静态链接库的所有全局变量的初始化顺序。


0

是的,除特定于实现的扩展外,main是每个C ++程序的“入口点”。即使这样,某些事情也会在main之前发生,尤其是全局初始化之前,例如main_ret。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.