C++中的main函数可以调用自己吗?

4szc88ey  于 2023-05-20  发布在  其他
关注(0)|答案(9)|浏览(185)

谁能告诉我下面的代码有什么问题吗?

int main () { 
    return main(); 
}

我测试过了,编译正确。它永远在运行。幕后还有什么诡计吗?

13z8s7eq

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时会发生什么?”就是“任何事”
    我希望这能澄清一些事情。
moiiocjp

moiiocjp2#

在C++中调用main是非法的(§3.6.1.3):
函数main不得在程序中使用。
您的编译器允许非法行为。
它会永远循环下去,因为main调用mainmain调用mainmain调用main,以此类推。

20jt8wwn

20jt8wwn3#

这就像是一个毒贩。相当非法,但编译,甚至工作了一段时间好...

r6hnlfcb

r6hnlfcb4#

当然,如果你真的想递归调用你的main函数,有时候有很好的理由,你应该这样做

int mymain()
{
  return mymain();
}

int main()
{
   return mymain();
}
icnyk63a

icnyk63a5#

问题是,你为什么要这么做?
main应该是程序的单一入口点。再次调用它实质上是重新启动程序,但没有新的流程示例;没有新的栈、没有新的堆等。
如果你真的需要递归,递归地调用一个单独的函数。

goucqfw6

goucqfw66#

C++标准在3.6.1节中说:
在程序中不得使用main函数(3.2)。
它指定,你不应该从你的程序中调用它。

9gm1akwq

9gm1akwq7#

当你写递归代码时,你需要确保在某个时候你停止了递归,否则你就写了一个无限循环。
你已经有了
你应该预料到这段代码会消失很长一段时间,最后会因为堆栈溢出而崩溃。

zqdjd7g9

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
}
e4eetjau

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;
 }


将退出。

相关问题