是'int main;' 一个有效的C/C++程序?

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的推理会有所不同:

  • C++使用名称修改来帮助链接器区分不同类型的文本相同的符号,例如全局变量xyz和独立的全局函数xyz(int).但是,名称main永远不会被破坏.
  • C不使用修改,因此程序可能通过提供一种符号来代替不同的符号来混淆链接器,并使程序成功链接.

这就是这里发生的事情:链接器期望找到符号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为某些未知原因定义变量,编译器会释放其他用途的名称.

  • @nm我认为你对这个问题的解释太狭隘了:除了在帖子的标题中提出问题之外,OP还清楚地寻求解释为什么他的程序首先编译("我的编译器似乎这么认为,即使我不")以及为什么将`main`定义为函数以外的任何东西都是有用的建议.答案提供了两个部分的解释. (4认同)
  • 虽然,C++编译器对主要功能的处理方式不同.即使没有外部"C",它的名字也不会被破坏.我想这是因为否则它需要发出它自己的外部"C"主,以确保链接. (3认同)
  • 实际上,C++和C*的结果并没有不同,正如这里所指出的那样 - "main"在C++中不受名称修改(看起来似乎)的影响,无论它是否是函数. (2认同)

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:存在使用该标识符的声明重新定义其含义的范围.

  • `cin`和`endl`不在默认命名空间中 - 它们位于`std`命名空间中.`npos`是`std :: basic_string`的成员. (7认同)

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.

  • "三个标准参数(argc,argv和envp)" - 这里你可能正在讨论Posix标准. (3认同)

Mic*_*nda 9

这是一个有效的计划吗?

没有.

它不是一个程序,因为它没有可执行的部分.

编译是否有效?

是.

它可以用于有效的程序吗?

是.

并非所有已编译的代码都必须可执行才有效.示例是静态和动态库.

您已经有效地构建了一个目标文件.它不是有效的可执行文件,但是另一个程序可以main通过在运行时加载它来链接到结果文件中的对象.

这应该是一个错误吗?

传统上,C++允许用户执行可能看起来没有有效用途但符合语言语法的事情.

我的意思是肯定的,这可能会被重新归类为错误,但为什么呢?警告不起作用的目的是什么?

只要理论上有可能在实际代码中使用此功能,那么main根据语言调用非函数对象就不太可能导致错误.


Ing*_*upp 6

我想通过引用实际语言标准来添加已经给出的答案.

是'int main;' 一个有效的C程序?

简短回答(我的观点):只有您的实施使用"独立执行环境".

所有以下来自C11的报价

5.环境

实现转换C源文件并在两个数据处理系统环境中执行C程序,这将被称为转换环境和执行环境[...]

5.1.2执行环境

定义了两个执行环境:独立和托管.在这两种情况下,当执行环境调用指定的C函数时,程序启动发生.

5.1.2.1独立环境

在独立环境中(可以在没有操作系统任何好处的情况下执行C程序),程序启动时调用的函数的名称和类型是实现定义的.

5.1.2.2托管环境

不需要提供托管环境,但如果存在,则应符合以下规范.

5.1.2.2.1程序启动

程序启动时调用的函数名为main.[...]它应定义为返回类型为int且没有参数[...]或具有两个参数[...]或等效或以某种其他实现定义的方式.

从中可以看出以下情况:

  • C11程序可以具有独立或托管执行环境并且有效.
  • 如果它具有独立式,则不需要存在主要功能.
  • 否则,必须有一个类型为int的返回值.

在一个独立的执行环境中,我认为它是一个不允许启动发生的有效程序,因为5.1.2中没有要求的功能.在托管执行环境中,虽然你的代码引入了一个名为main的对象,但是它不能提供返回值,所以我认为它在这个意义上不是一个有效的程序,尽管如果程序不是这样的话,人们也可以说意味着要执行(例如,可能只想提供数据),那么它只是不允许这样做.

是'int main;' 一个有效的C++程序?

简短回答(我的观点):只有您的实施使用"独立执行环境".

引自C++ 14

3.6.1主要功能

程序应包含一个名为main的全局函数,它是程序的指定开始.实现定义是否需要独立环境中的程序来定义主函数.[...]它应具有int类型的返回类型,否则其类型是实现定义的.[...]名称main不以其他方式保留.

这里,与C11标准相反,对独立执行环境的限制较少,因为根本没有提到启动功能,而对于托管执行环境,情况与C11几乎相同.

同样,我认为对于托管案例,您的代码不是有效的C++ 14程序,但我确信它是针对独立案例的.

由于我的回答只考虑执行环境,我认为dasblinkenlicht的答案发挥作用,因为翻译环境中出现的名称错误事先发生.在这里,我不太确定如此严格地遵守上述引用.