Geo*_*xon 111 c c++ program-entry-point function entry-point
我问,因为我的编译器似乎这么认为,即使我没有.
echo 'int main;' | cc -x c - -Wall
echo 'int main;' | c++ -x c++ - -Wall
Clang没有发出任何警告或错误,gcc只发出温和的警告:'main' is usually a function [-Wmain]但是只有在编译为C时才指定.指定a -std=似乎并不重要.
否则,它编译和链接很好.但在执行时,它立即终止SIGBUS(对我来说).
阅读(C)和C++中main()返回的(优秀)答案?和快速的grep通过语言规范,这肯定会似乎对我来说,一个主要的功能是必需的.但是gcc -Wmain('main' 通常是一个函数)的冗语(以及这里的错误缺失)似乎可能暗示其他情况.
但为什么?是否有一些奇怪的边缘案例或"历史"用途?谁知道什么给了?
我想,我的观点是,我认为这应该是托管环境中的错误,是吗?
das*_*ght 97
由于这个问题被双重标记为C和C++,因此C++和C的推理会有所不同:
xyz和独立的全局函数xyz(int).但是,名称main永远不会被破坏.这就是这里发生的事情:链接器期望找到符号main,它确实如此.它将该符号"连线"就好像它是一个函数,因为它不知道更好.运行时库的一部分,它将控制权传递给main链接器main,因此链接器为其提供符号main,让链接阶段完成.当然,这在运行时失败,因为main它不是一个函数.
这是同一问题的另一个例子:
文件xc:
#include <stdio.h>
int foo(); // <<== main() expects this
int main(){
printf("%p\n", (void*)&foo);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
档案yc:
int foo; // <<== external definition supplies a symbol of a wrong kind
Run Code Online (Sandbox Code Playgroud)
编译:
gcc x.c y.c
Run Code Online (Sandbox Code Playgroud)
这可以编译,它可能会运行,但它是未定义的行为,因为承诺给编译器的符号类型与提供给链接器的实际符号不同.
就警告而言,我认为这是合理的:C允许您构建没有main功能的库,因此main如果您需要main为某些未知原因定义变量,编译器会释放其他用途的名称.
man*_*lio 30
main是不是保留字,它只是一个预定义的标识符(如cin,endl,npos...),所以你可以声明称为变量main,初始化它,然后打印出它的价值.
当然:
main()函数的源文件(库).编辑
一些参考:
main 不是保留字(C++ 11):
该功能
main不得在程序中使用.链接(3.5)main是实现定义的.为删除或声明主限定主一种程序,是inline,static,或者constexpr是形成不良的.该名称main未另行保留.[示例:可以调用成员函数,类和枚举main,其他名称空间中的实体也可以调用. - 结束例子]
C++ 11 - [basic.start.main] 3.6.1.3
[2.11/3] [...]某些标识符保留供C++实现和标准库(17.6.4.3.2)使用,否则不得使用; 无需诊断.
[17.6.4.3.2/1]某些名称和功能签名集始终保留给实施:
- 每个包含双下划线__的名称或以下划线后跟大写字母(2.12)开头的名称都保留给实现以供任何使用.
- 以下划线开头的每个名称都保留给实现,以用作全局名称空间中的名称.
程序员可能不会重新定义保留字,但通常可以在某些容量中覆盖预定义字.这是这样的情况main:存在使用该标识符的声明重新定义其含义的范围.
n. *_* m. 19
是int main;一个有效的C/C++程序?
目前还不完全清楚C/C++程序是什么.
是int main;一个有效的C程序?
是.允许独立实施接受此类计划.main在独立环境中不必具有任何特殊含义.
这是不是在托管环境中有效.
是int main;一个有效的C++程序?
同上.
为什么会崩溃?
该程序无需在您的环境中有意义.在独立环境中,程序启动和终止以及其含义main是实现定义的.
为什么编译器会警告我?
编译器可能会警告您不管它喜欢什么,只要它不拒绝合规程序.另一方面,警告是诊断不合格程序所需的全部内容.由于此转换单元不能是有效托管程序的一部分,因此诊断消息是合理的.
是gcc一个独立的环境,还是托管环境?
是.
gcc记录-ffreestanding编译标志.添加它,警告消失.您可能希望在构建内核或固件时使用它.
g++没有记录这样的旗帜.提供它似乎对此程序没有影响.假设g ++提供的环境是托管的,可能是安全的.在这种情况下缺乏诊断是一个错误.
das*_*ndy 17
这是一个警告,因为它在技术上是不允许的.启动代码将使用"main"的符号位置,并使用三个标准参数(argc,argv和envp)跳转到它.它没有,并且在链接时不能检查它实际上是一个函数,甚至它没有那些参数.这也是int main(int argc,char**argv)工作的原因 - 编译器不知道envp参数,它恰好不会被使用,而且它是调用者清理的.
作为一个笑话,你可以做点什么
int main = 0xCBCBCBCB;
Run Code Online (Sandbox Code Playgroud)
在x86机器上,忽略警告和类似的东西,它不仅会编译,但实际上也可以工作.
有人使用类似于此的技术编写可直接在多个体系结构上运行的可执行文件(http://phrack.org/issues/57/17.html#article).它也被用来赢得IOCCC - http://www.ioccc.org/1984/mullender/mullender.c.
这是一个有效的计划吗?
没有.
它不是一个程序,因为它没有可执行的部分.
编译是否有效?
是.
它可以用于有效的程序吗?
是.
并非所有已编译的代码都必须可执行才有效.示例是静态和动态库.
您已经有效地构建了一个目标文件.它不是有效的可执行文件,但是另一个程序可以main通过在运行时加载它来链接到结果文件中的对象.
这应该是一个错误吗?
传统上,C++允许用户执行可能看起来没有有效用途但符合语言语法的事情.
我的意思是肯定的,这可能会被重新归类为错误,但为什么呢?警告不起作用的目的是什么?
只要理论上有可能在实际代码中使用此功能,那么main根据语言调用非函数对象就不太可能导致错误.
我想通过引用实际语言标准来添加已经给出的答案.
简短回答(我的观点):只有您的实施使用"独立执行环境".
所有以下来自C11的报价
5.环境
实现转换C源文件并在两个数据处理系统环境中执行C程序,这将被称为转换环境和执行环境[...]
5.1.2执行环境
定义了两个执行环境:独立和托管.在这两种情况下,当执行环境调用指定的C函数时,程序启动发生.
5.1.2.1独立环境
在独立环境中(可以在没有操作系统任何好处的情况下执行C程序),程序启动时调用的函数的名称和类型是实现定义的.
5.1.2.2托管环境
不需要提供托管环境,但如果存在,则应符合以下规范.
5.1.2.2.1程序启动
程序启动时调用的函数名为main.[...]它应定义为返回类型为int且没有参数[...]或具有两个参数[...]或等效或以某种其他实现定义的方式.
从中可以看出以下情况:
在一个独立的执行环境中,我认为它是一个不允许启动发生的有效程序,因为5.1.2中没有要求的功能.在托管执行环境中,虽然你的代码引入了一个名为main的对象,但是它不能提供返回值,所以我认为它在这个意义上不是一个有效的程序,尽管如果程序不是这样的话,人们也可以说意味着要执行(例如,可能只想提供数据),那么它只是不允许这样做.
简短回答(我的观点):只有您的实施使用"独立执行环境".
引自C++ 14
3.6.1主要功能
程序应包含一个名为main的全局函数,它是程序的指定开始.实现定义是否需要独立环境中的程序来定义主函数.[...]它应具有int类型的返回类型,否则其类型是实现定义的.[...]名称main不以其他方式保留.
这里,与C11标准相反,对独立执行环境的限制较少,因为根本没有提到启动功能,而对于托管执行环境,情况与C11几乎相同.
同样,我认为对于托管案例,您的代码不是有效的C++ 14程序,但我确信它是针对独立案例的.
由于我的回答只考虑执行环境,我认为dasblinkenlicht的答案发挥作用,因为翻译环境中出现的名称错误事先发生.在这里,我不太确定如此严格地遵守上述引用.