谁能告诉我下面的代码有什么问题吗?
int main () { return main(); }
我测试过了,编译正确。它永远在运行。幕后还有什么诡计吗?
13z8s7eq1#
TLDR:调用main会导致未定义的行为。
main
标准中使用的术语以及对程序员和编译器的影响似乎存在混淆。首先,标准本身决定了 C++ 的一切。如果你的特定版本的特定编译器允许一些特定的动作,这与该动作是否法律的无关。在本文的其余部分,我指的是ISO 03标准。因此,再次引用标准§ 3.6.1.3:函数main不得在程序中使用。此外,§3.2将“使用”定义为:如果对象或非重载函数的名称出现在可能计算的表达式中,则使用该对象或函数。这意味着一旦程序开始执行,main就应该不再输入。这意味着程序员不能调用main,这意味着编译器不能插入另一个对main的调用(为什么会这样,谁知道呢),你不能获取main的地址并调用它,等等。您甚至不能调用main。对main的唯一调用应该是由程序运行的运行时库进行的;所有其它调用调用未定义的行为。(这意味着任何事情都可能发生!)现在来看看编译器的行为:可诊断规则定义为(§1.4.1):可诊断规则集包括本标准中的所有语法和语义规则,但不包括那些包含明确表示“不需要诊断”或被描述为导致“未定义行为”的规则。在我们的例子中,§3.6.1.3定义了一个可诊断规则。下面是编译器根据§1.4.2应该做的事情:
-pedantic
moiiocjp2#
在C++中调用main是非法的(§3.6.1.3):函数main不得在程序中使用。您的编译器允许非法行为。它会永远循环下去,因为main调用main,main调用main,main调用main,以此类推。
20jt8wwn3#
这就像是一个毒贩。相当非法,但编译,甚至工作了一段时间好...
r6hnlfcb4#
当然,如果你真的想递归调用你的main函数,有时候有很好的理由,你应该这样做
int mymain() { return mymain(); } int main() { return mymain(); }
icnyk63a5#
问题是,你为什么要这么做?main应该是程序的单一入口点。再次调用它实质上是重新启动程序,但没有新的流程示例;没有新的栈、没有新的堆等。如果你真的需要递归,递归地调用一个单独的函数。
goucqfw66#
C++标准在3.6.1节中说:在程序中不得使用main函数(3.2)。它指定,你不应该从你的程序中调用它。
9gm1akwq7#
当你写递归代码时,你需要确保在某个时候你停止了递归,否则你就写了一个无限循环。你已经有了你应该预料到这段代码会消失很长一段时间,最后会因为堆栈溢出而崩溃。
zqdjd7g98#
虽然你的程序显然是没有意义的,因为它永远循环,但看起来可行的做法是:
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 }
e4eetjau9#
你有两个问题。第一个是调用main,正如已经指出的那样,它既违反了标准,也违反了标准的意图。更大的问题是你写了一个没有任何结束点的递归调用。你的问题似乎假设初始版本调用的main版本将返回。然而,大多数语言(实际上我能想到的所有语言)都允许无限递归:如果一个函数调用它自己,那么这个版本也会调用它自己。唯一的限制是系统资源。因此,您需要将调用 Package 在条件中,并仅在需要时继续调用。在您的示例中,将一个全局整数集添加到您想要递归的级别数将有效。就像这样:”
int levels = 3; int main() { if(levels) { cout << "Recursing another level...\n"; levels--; main(); } else { cout << "Reached the bottom.\n"; } return levels; }
”将退出。
9条答案
按热度按时间13z8s7eq1#
TLDR:调用
main
会导致未定义的行为。标准中使用的术语以及对程序员和编译器的影响似乎存在混淆。
首先,标准本身决定了 C++ 的一切。如果你的特定版本的特定编译器允许一些特定的动作,这与该动作是否法律的无关。在本文的其余部分,我指的是ISO 03标准。
因此,再次引用标准§ 3.6.1.3:
函数main不得在程序中使用。
此外,§3.2将“使用”定义为:
如果对象或非重载函数的名称出现在可能计算的表达式中,则使用该对象或函数。
这意味着一旦程序开始执行,
main
就应该不再输入。这意味着程序员不能调用main
,这意味着编译器不能插入另一个对main
的调用(为什么会这样,谁知道呢),你不能获取main的地址并调用它,等等。您甚至不能调用main
。对
main
的唯一调用应该是由程序运行的运行时库进行的;所有其它调用调用未定义的行为。(这意味着任何事情都可能发生!)现在来看看编译器的行为:
可诊断规则定义为(§1.4.1):
可诊断规则集包括本标准中的所有语法和语义规则,但不包括那些包含明确表示“不需要诊断”或被描述为导致“未定义行为”的规则。
在我们的例子中,§3.6.1.3定义了一个可诊断规则。下面是编译器根据§1.4.2应该做的事情:
所以编译器不需要强制执行规则。编译器所要做的就是把 * 格式良好的程序 *(1.3.14节)转化为可执行程序。编译程序可以自由地发出警告、出错等。不管它喜欢什么,只要它不与语言冲突。根据第二个子句,在我们的特定情况下,显示消息是“必需的”。
对于这个特殊的问题,gcc上的
-pedantic
选项会警告在程序中调用main
是非法的。Visual Studio不会对调用main
发出警告,但在任何警告级别(大于0),它都会对程序的递归性质发出警告。从你应该期待的答案来看,这一切意味着什么?这意味着试图确定发布的代码片段将做什么是完全没有意义的。调用
main
会导致未定义的行为,尝试定义未定义的行为显然是一个失败的原因。对于“当我调用main
时会发生什么?”就是“任何事”我希望这能澄清一些事情。
moiiocjp2#
在C++中调用
main
是非法的(§3.6.1.3):函数main不得在程序中使用。
您的编译器允许非法行为。
它会永远循环下去,因为
main
调用main
,main
调用main
,main
调用main
,以此类推。20jt8wwn3#
这就像是一个毒贩。相当非法,但编译,甚至工作了一段时间好...
r6hnlfcb4#
当然,如果你真的想递归调用你的main函数,有时候有很好的理由,你应该这样做
icnyk63a5#
问题是,你为什么要这么做?
main应该是程序的单一入口点。再次调用它实质上是重新启动程序,但没有新的流程示例;没有新的栈、没有新的堆等。
如果你真的需要递归,递归地调用一个单独的函数。
goucqfw66#
C++标准在3.6.1节中说:
在程序中不得使用main函数(3.2)。
它指定,你不应该从你的程序中调用它。
9gm1akwq7#
当你写递归代码时,你需要确保在某个时候你停止了递归,否则你就写了一个无限循环。
你已经有了
你应该预料到这段代码会消失很长一段时间,最后会因为堆栈溢出而崩溃。
zqdjd7g98#
虽然你的程序显然是没有意义的,因为它永远循环,但看起来可行的做法是:
然而,标准不允许它。(见其他答案)
当然你可以通过这样做来实现
e4eetjau9#
你有两个问题。第一个是调用main,正如已经指出的那样,它既违反了标准,也违反了标准的意图。
更大的问题是你写了一个没有任何结束点的递归调用。你的问题似乎假设初始版本调用的main版本将返回。然而,大多数语言(实际上我能想到的所有语言)都允许无限递归:如果一个函数调用它自己,那么这个版本也会调用它自己。唯一的限制是系统资源。
因此,您需要将调用 Package 在条件中,并仅在需要时继续调用。在您的示例中,将一个全局整数集添加到您想要递归的级别数将有效。就像这样:
”
”
将退出。