C++如何将'main'声明为一个函数的引用?

8
如果将main定义为函数的引用,会有什么影响呢?
#include<iostream>
#include<cstring>

using namespace std;

int main1()
{
    cout << "Hello World from main1 function!" << endl;
    return 0;
}

int (&main)() = main1;

会发生什么?我在一个在线编译器中测试时出现了“分段错误”的错误:

这里

在VC++ 2013下它将创建一个在运行时崩溃的程序!

一段调用函数指针数据作为代码的代码将被编译,这将在启动时立即崩溃。

我还想引用ISO C++标准上的相关内容。

如果想要根据某个宏定义来定义两个入口点中的任何一个,那么这个概念将是有用的,例如:

int main1();

int main2();

#ifdef _0_ENTRY
int (&main)() = main1;
#else
int (&main)() = main2;
#endif

2
我认为你期望ISO标准评论这种疯狂行为有点过分。 - David Hoelzer
1
有趣的是,标准实际上并不禁止在独立实现中构建的代码定义一个名为“main”的全局符号,尽管它在该命名空间中对该符号施加了其他限制。 - Lightness Races in Orbit
1
@ChristianHackl 标准已经超过1300页了。如果它必须详细说明程序员想要做的每一件疯狂的事情,那么没有人会去阅读它。 - user3920237
1
@remyabel:事实上,正如问题的答案所显示的那样,标准确实定义了是否允许OP的代码。它必须这样做;根据定义,标准涵盖了每一个可以想象的代码片段,并告诉您它是否有效。换句话说,没有任何一段代码,无论多么疯狂,标准都会说“我不在乎这是否是未定义行为”。 - Christian Hackl
2
@remyabel:在[basic.start.main]/3中写道:“函数main不应在程序内使用。”这包括将其用于初始化引用。(并不是说这与问题有任何关系,问题是关于允许main成为什么类型的实体。) - Mike Seymour
显示剩余13条评论
5个回答

16
这不是符合C++标准的程序。 在C++中,必须有一个名为main的全局函数(参见章节3.6.1)。
您的程序包含了一个名为main但不是函数的全局变量,这会引入与所需的main函数名称冲突。
其中一个原因是它允许托管环境在程序启动期间调用main函数。这与源字符串main(args)不等价,后者可能是函数调用、函数指针取值、在函数对象上使用operator()或构造类型main的实例。所以,main必须是函数。
另外需要注意的一点是,C++标准从未说明main的实际类型,并且禁止您观察它。因此,实现可以(而且确实!)重写签名,例如添加您省略的任何int argc, char** argv, char** envp中的参数。显然,在您的main1main2中,它无法知道要这样做。

1
你的报价不完整。紧接着它声明:“在自由环境中,程序是否需要定义一个main函数是实现定义的。”而且没有规定这样的程序不能在全局命名空间中定义另一种名为“main”的实体(p3:“定义main为已删除或声明main为内联、静态或constexpr的程序是非法的。名称main不受保留。[例如:成员函数、类和枚举可以被称为main,其他命名空间中的实体也可以。——结束示例]”)。 - Lightness Races in Orbit
话虽如此,我认为我们可以假设 OP 没有使用这样的实现。:) 但也许所有这些都会改善这个答案? - Lightness Races in Orbit
@LightnessRacesinOrbit:有趣的是,他们选择将这个规则写成一个通用陈述后面跟着一个矛盾,甚至没有一个"除非"。他们肯定可以说:"托管环境中的程序应包含一个名为main的全局函数"或者"一个程序应包含一个名为main的全局函数,除非在独立环境下它是实现定义的..." - Ben Voigt
我同意。这个措辞一直让我感到不舒服(并且似乎会在随后被误解时引起一些互联网争论!)。 - Lightness Races in Orbit

7
这将在宏定义的情况下有用,以定义其中任意一个入口点。
不是很准确。你应该这样做:
int main1();
int main2();

#ifdef _0_ENTRY
   int main() { return main1(); }
#else
   int main() { return main2(); }
#endif

是的,但是对我来说,仅分配一个函数引用似乎更直观。否则,我们正在定义另一个不完全符合我的要求的函数。 - AnArrayOfFunctions
2
这是一个不错且不显眼的解决方案。 - Christian Hackl
@FISOCPP:我真的不明白为什么不行。此外,你没有选择的余地。 :) - Lightness Races in Orbit
我从未说过这是不好的。我只是觉得我的版本更直观。 - AnArrayOfFunctions
1
@FISOCPP:我不认为你的版本很直观,但我想每个人都有自己的看法。 - Lightness Races in Orbit
@Mike:谢谢,糟糕;)顺便说一下,请查看答案差异中的高亮显示;SO做得很好。 - Lightness Races in Orbit

3

由于CWG问题1886的解决,这很快将变得明显不正确,该问题目前处于“暂时准备就绪”状态,其中其他一些东西被添加到[basic.start.main]中,包括以下内容:

在全局范围内声明一个名为main的变量或者声明具有C语言链接的名称main(在任何命名空间中)是不正确的。


也许他们应该将对'main'函数的引用作为规则例外允许。 - AnArrayOfFunctions
3
这个实现过程将会变得非常复杂,因为 main 函数必须是一个简单的函数,可以被 C++ 运行时调用。引入像你所提议的那样的复杂性是不值得的。 - Lightness Races in Orbit

1
实际发生的情况高度依赖于实现。在您的情况下,编译器显然将该引用实现为“伪装的指针”。除此之外,指针具有外部链接。也就是说,您的程序导出了一个名为main的外部符号,实际上与数据段中占用的指针的内存位置相关联。链接器在不太深入地查看它的情况下,将该内存位置记录为程序的入口点。稍后,尝试将该位置用作入口点会导致分段错误。首先,在那个位置没有有意义的代码。其次,仅仅尝试将控制权传递到数据段内部的位置可能会触发平台的“数据执行保护”机制。通过这样做,您显然希望引用将被优化掉,即main将变成main1的另一个名称。在您的情况下,引用作为独立的外部对象幸存了下来。

0

看起来你已经回答了关于发生了什么的问题。

至于为什么,在你的代码中,main 是一个函数的引用/指针。这与函数不同。如果代码调用指针而不是函数,我会期望发生段错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接