有谁可以告诉下面的代码有什么问题?
int main () {
return main();
}
我测试过,它编译正确。它一直在运行。现场背后的诀窍呢?
答案 0 :(得分:36)
TLDR :调用main
会导致未定义的行为。
标准中使用的术语似乎存在混淆,以及对程序员和编译器的影响。
首先,标准决定了C ++语言的一切。如果特定编译器的特定版本允许某些特定操作,则与该操作是否合法无关。对于帖子的其余部分,我指的是ISO03标准。
所以再次引用,标准在§3.6.1.3中说:
函数main不得在程序中使用。
此外,§3.2将“used”定义为:
如果对象或非重载函数的名称出现在可能已评估的表达式中,则使用该函数。
这意味着一旦程序开始执行,main
永远不会再次输入。这意味着程序员无法调用main
,这意味着编译器无法插入另一个调用main
(为什么会这样,谁知道),你不能取主要地址并调用它等等。你甚至不能有可能打电话给main
。
对main
的唯一调用应该是程序运行的运行时库;所有其他调用都会调用未定义的行为。 (这意味着任何事情都可能发生!)
现在进入编译器行为:
可诊断规则定义为(§1.4.1):
可诊断规则集包含本国际标准中的所有语法和语义规则,但那些包含“无需诊断”的明确表示法或描述为“未定义行为”的规则除外。
在我们的案例中,§3.6.1.3定义了可诊断的规则。以下是编译器应根据§1.4.2进行的操作:
- 如果某个程序不违反本国际标准中的规则,则符合条件的实施应在其资源限制内接受并正确执行该程序。
- 如果程序包含违反任何可诊断规则的行为,则符合要求的实施应至少发出一条诊断消息,但该除外 - 如果某个程序违反了不需要诊断的规则,则本国际标准不对该程序的实施提出任何要求。
因此编译器不需要强制执行规则。所有编译器必须做的是采用格式良好的程序(§1.3.14)并将它们转换为可执行程序。编译器可以自由地发出警告,错误等等,只要它不与语言冲突。根据第二个条款, required 在我们的特定情况下显示消息。
对于这个特殊问题,在gcc上-pedantic
选项会警告在程序中调用main
的非法性。 Visual Studio不会警告调用main
,但在任何警告级别(大于0)上它都会警告该程序的递归性质。
对于您应该期待的答案,这一切意味着什么?这意味着尝试确定代码片段发布的内容是完全没有意义的。调用main
会导致未定义的行为,并且尝试定义未定义的行为显然是一个失败的原因。任何人都可以给出的唯一诚实的答案是“当我致电main
时会发生什么?”是“一切。”
我希望这能解决问题。
答案 1 :(得分:24)
在C ++中调用main
是非法的(§3.6.1.3):
函数main不得在程序中使用。
您的编译器允许非法行为。
它永远循环,因为main
调用main
,调用main
,调用main
,依此类推。
答案 2 :(得分:9)
就像是一个贩毒者。非常非法,但编译甚至可以在一段时间内发挥作用......
答案 3 :(得分:4)
问题是,你为什么要这样做?
main应该是您程序的单个入口点。再次调用它本质上会重新启动您的程序,但没有新的流程实例;没有新堆栈,没有新堆等。
如果您确实需要递归,请递归调用单独的函数。
答案 4 :(得分:4)
C ++标准,在3.6.1节中说:
在程序中不得使用函数main(3.2)。
它指定,您不应该在程序中调用它。
答案 5 :(得分:4)
当然如果你真的想要递归调用你的main函数,有时候有充分的理由,你应该这样做
int mymain()
{
return mymain();
}
int main()
{
return mymain();
}
答案 6 :(得分:2)
当您编写递归代码时,您需要确保在某些时候停止递归,否则您只是编写了一个无限循环。
你有哪些。
您应该期望此代码在 long 时间消失,然后最终因堆栈溢出而崩溃。
答案 7 :(得分:1)
你有两个问题。第一个是调用main,正如已经指出的那样,它们都违反了标准的标准和意图。
更大的问题是你写了一个没有任何关闭点的递归调用。你的问题似乎假设初始版本调用的main版本将返回。但是,大多数语言(事实上我只能想到)允许无限递归:如果函数调用自身,那么该版本也将如此。唯一的限制是系统资源。
因此,您需要在条件中包装调用,并在需要时继续调用。在您的示例中,将全局整数集添加到要递归的级别数将起作用。像这样:
`
int levels = 3;
int main() {
if(levels) {
cout << "Recursing another level...\n";
levels--;
main();
}
else {
cout << "Reached the bottom.\n";
}
return levels;
}
`
将退出。
答案 8 :(得分:0)
虽然你的程序显然没有任何意义,因为它永远循环,但它可能看似可行:
int main( int argc, char* argv[] )
{
if( argc )
{
// do something with argv[0]
main( argc - 1, &argv[1] );
}
else
{
// rest of application having loaded your arguments
}
}
但是标准不允许这样做。 (见其他答案)
当然,你可以通过这样做来实现它
int myFunc( int argc, char * argv[] )
{
// I can recurse this if I like and just get main to forward straight to here
}