具有名为 main 的全局变量而不是 main 函数的程序如何工作?

How can a program with a global variable called main instead of a main function work?

考虑以下程序:

#include <iostream>
int main = ( std::cout << "C++ is excellent!\n", 195 ); 

在Windows 7 OS上使用g++ 4.8.1 (mingw64),程序编译运行良好,打印:

C++ is excellent!

到控制台。 main 似乎是一个全局变量而不是一个函数;如果没有函数 main(),该程序如何执行?这段代码是否符合 C++ 标准?程序的行为是否定义明确?我也使用了 -pedantic-errors 选项,但程序仍然可以编译和运行。

从 3.6.1/1 开始:

A program shall contain a global function called main, which is the designated start of the program. It is implementation defined whether a program in a freestanding environment is required to define a main function.

由此看来,g++ 恰好允许没有主函数的程序(大概是 "freestanding" 子句)。

然后从 3.6.1/3 开始:

The function main shall not be used (3.2) within a program. The linkage (3.5) of main is implementation defined. A program that declares main to be inline or static is illformed. The name main is not otherwise reserved.

所以在这里我们了解到,有一个名为 main.

的整数变量是非常好的

最后,如果您想知道为什么要打印输出,int main 的初始化使用逗号运算符在静态初始化时执行 cout,然后提供一个实际的整数值来执行初始化。

这是一个错误的程序。它在我的测试环境 cygwin64/g++ 4.9.3.

中崩溃

来自标准:

3.6.1 Main function [basic.start.main]

1 A program shall contain a global function called main, which is the designated start of the program.

在深入探讨正在发生的事情之前,重要的是要指出程序的格式不正确 defect report 1886: Language linkage for main():

[...] A program that declares a variable main at global scope or that declares the name main with C language linkage (in any namespace) is ill-formed. [...]

最新版本的 clang 和 gcc 导致此错误,程序无法编译 (see gcc live example):

error: cannot declare '::main' to be a global variable
int main = ( std::cout << "C++ is excellent!\n", 195 ); 
    ^

那么为什么在旧版本的 gcc 和 clang 中没有诊断?该缺陷报告直到 2014 年底才提出解决方案,因此该案例直到最近才明确显示格式错误,需要进行诊断。

在此之前,这似乎是未定义的行为,因为我们违反了 C++ 标准草案 3.6.1 部分的 shall 要求][basic.start.main]:

A program shall contain a global function called main, which is the designated start of the program. [...]

未定义的行为是不可预测的,不需要诊断。我们在重现行为时看到的不一致是典型的未定义行为。

那么代码实际上在做什么,为什么在某些情况下会产生结果?让我们看看我们有什么:

declarator  
|        initializer----------------------------------
|        |                                           |
v        v                                           v
int main = ( std::cout << "C++ is excellent!\n", 195 ); 
    ^      ^                                   ^
    |      |                                   |
    |      |                                   comma operator
    |      primary expression
global variable of type int

我们有main,它是在全局命名空间中声明的int,正在初始化,该变量具有静态存储持续时间。初始化是否在尝试调用 main 之前发生是实现定义的,但看起来 gcc 在调用 main 之前确实这样做了。

代码使用 comma operator,左操作数是一个废弃的值表达式,这里仅用于调用 std::cout 的副作用。逗号运算符的结果是右操作数,在本例中是分配给变量 main.

的纯右值 195

我们可以看到 the generated assembly shows that cout is called during static initialization. Although the more interesting point for discussion see live godbolt session会是这样的:

main:
.zero   4

及后续:

movl    5, main(%rip)

可能的情况是程序跳转到符号 main 并期望有效代码存在于 some cases will seg-fault. So if that is the case we would expect storing valid machine code in the variable main could lead to workable program, assuming we are located in a segment that allows code execution. We can see this 1984 IOCCC entry does just that.

看来我们可以让 gcc 在 C 中使用 (see it live):

const int main = 195 ;

如果变量 main 不是 const,它会出现段错误,大概是因为它不在可执行位置,Hat Tip to this comment here 这给了我这个想法。

另见 此问题的特定 C 版本。

我认为这可行的原因是 编译器 不知道它正在编译 main() 函数,因此它编译了一个具有赋值副作用的全局整数。

这个 t运行slation-unit 编译成的 object 格式 无法区分 函数符号和一个变量符号.

因此 linker 愉快地链接到(变量)main 符号并将其视为函数调用。但直到运行时间系统有运行全局变量初始化代码。

当我 运行 样本打印出来时,它导致了 seg-fault。我假设那是 运行time system 试图执行一个 int 变量 就好像它是一个 function.

我已经在 Win7 64 位 OS 上使用 VS2013 进行了尝试,它编译正确,但是当我尝试构建应用程序时,我从输出 window.

1>------ Build started: Project: tempTest, Configuration: Debug Win32 ------
1>LINK : fatal error LNK1561: entry point must be defined
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

gcc 4.8.1 生成以下 x86 程序集:

.LC0:
    .string "C++ is excellent!\n"
    subq    , %rsp    #,
    movl    std::__ioinit, %edi #,
    call    std::ios_base::Init::Init() #
    movl    $__dso_handle, %edx #,
    movl    std::__ioinit, %esi #,
    movl    std::ios_base::Init::~Init(), %edi  #,
    call    __cxa_atexit    #
    movl    $.LC0, %esi #,
    movl    std::cout, %edi #,
    call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)   #
    movl    5, main(%rip)    #, main
    addq    , %rsp    #,
    ret
main:
    .zero   4

注意cout是在初始化时调用的,不是在main函数中调用的!

.zero 4 声明从位置 main 开始的 4 个(0 初始化)字节, 其中 main 变量的名称[!].

main 符号被解释为程序的开始。 行为取决于平台。

你在这里做着棘手的工作。因为 main( 不知何故) 可以声明为整数。您使用列表运算符打印消息,然后将 195 分配给它。正如下面有人所说,它不适用于 C++,这是真的。但是由于编译器没有找到任何用户定义的名称 main,所以它没有抱怨。记住 main 不是系统定义的函数,它的用户定义函数和程序开始执行的东西是主模块,而不是 main(),特别是。 main() 再次被加载程序有意执行的启动函数调用。然后你所有的变量都被初始化,&在初始化它的时候输出这样的。而已。没有main()的程序可以,但不标准。