【问题标题】:Why does this recursive method cause a Stack Overflow error when it has no variables?为什么这种递归方法在没有变量时会导致堆栈溢出错误?
【发布时间】:2022-01-01 23:59:22
【问题描述】:

我有这样的递归方法,它不包含任何变量。为什么会抛出堆栈溢出异常?

class MainClass
{
    static void Main() => Bark();

    static void Bark() { Bark(); }
}

在上面的例子中,我没有创建任何变量。如果我创建任何变量(作为参数或在方法内部),那么这是可以理解的:在线程的堆栈中已经创建了许多变量,并且由于内存不足,我得到了一个错误。

我不明白,是不是方法本身也存储在堆栈中?为什么我会收到错误消息?

【问题讨论】:

  • 返回地址也存储在栈上。方法需要知道从哪里调用它才能返回该代码。
  • 如果我理解错了,请纠正我:你说堆栈也存储方法的地址?
  • 不是方法的,而是被调用的方法返回时要恢复执行的代码地址。
  • 标志也被推送。
  • 你的代码在不停地吠叫。您引入了一个没有基本情况的递归。

标签: c# multithreading memory stack stack-overflow


【解决方案1】:

如果您要调试这段代码并查看“调用堆栈”窗口,您会看到它无限次尝试将Bark 添加到调用堆栈,因为递归没有终点。

【讨论】:

  • 感谢您提供信息。
【解决方案2】:

我相信您期望看到的是尾递归。不幸的是 C# 编译器不支持它。

【讨论】:

  • 那么,在您看来,在启用尾调用递归消除的情况下,正如我认为您所指的,编译器应该生成直接导致堆栈溢出的代码,还是只是一个无限循环?跨度>
  • @500-InternalServerError 是的,我认为会发生无限循环。您的程序不会出现内存错误,而是会挂起。
  • 是的,我等待无限循环,没有任何错误。并感谢回答。
【解决方案3】:

堆栈帧不仅包含参数,还包含一个返回地址,以便处理器知道返回到哪里。

此外,隐藏的this指针也是一个参数。要删除它,您需要一个 static 函数。

还有 ebp 或其他堆栈帧指针,可以在每次调用时将其压入堆栈,具体取决于确切的调用约定。

所以无论你做什么,你在某个时候肯定会发生堆栈溢出,除非编译器决定执行尾递归。

【讨论】:

  • 感谢您的回答。
猜你喜欢
  • 2021-01-26
  • 2019-11-18
  • 2014-12-20
  • 1970-01-01
  • 1970-01-01
  • 2011-02-08
  • 1970-01-01
  • 2021-04-19
  • 2018-10-29
相关资源
最近更新 更多