在深入探讨正在发生的问题之前,必须指出,根据缺陷报告1886:main()的语言链接,程序是格式错误的:
[...]在全局范围内声明变量main或使用C语言链接(在任何命名空间中)声明main名称的程序格式错误。[...]
最新版本的clang和gcc导致此错误,并且程序将无法编译(请参见gcc live示例):
error: cannot declare '::main' to be a global variable
int main = ( std::cout << "C++ is excellent!\n", 195 );
^
那么,为什么在旧版本的gcc和clang中没有诊断程序?该缺陷报告甚至在2014年底之前都没有提出解决方案,因此该案例直到最近才被明确发现其病态严重,需要进行诊断。
在此之前,这似乎是未定义的行为,因为我们违反了[basic.start.main]部分中C ++标准草案的规定要求:3.6.1
程序应包含一个称为main的全局功能,这是程序的指定启动位置。[...]
未定义的行为是不可预测的,不需要诊断。我们看到的与重现行为的不一致是典型的未定义行为。
那么代码实际上在做什么?为什么在某些情况下会产生结果?让我们看看我们有什么:
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
。
该代码使用逗号运算符,左操作数是一个舍弃的值表达式,在这里仅用于调用的副作用std::cout
。逗号运算符的结果是右操作数,在这种情况下,右运算数195
是分配给变量的prvalue main
。
我们可以看到sergej指出了cout
在静态初始化期间调用的生成的程序集显示。尽管讨论现场直播更有趣的一点是:
main:
.zero 4
随后:
movl $195, main(%rip)
可能的情况是该程序跳转到main
期望有效代码的符号,并且在某些情况下将出现seg-fault。因此,在这种情况下,假设我们位于允许代码执行的段中,那么我们期望在变量中存储有效的机器代码main
可能会导致程序可行。我们可以看到,这个1984年进入IOCCC不只是。
看来我们可以让gcc在C中使用(现场观看)进行此操作:
const int main = 195 ;
据赛格故障,如果变量main
不是常量大概是因为它并不位于一个可执行文件的位置,帽尖这个评论这里这给了我这个想法。
另请参见FUZxxl对此问题的特定于C版本的答案。