【问题标题】:Could a brute-force algorithm cause a stack overflow? (recursion)蛮力算法会导致堆栈溢出吗? (递归)
【发布时间】:2015-12-08 20:33:10
【问题描述】:

假设我们有一个递归蛮力函数。
(我特别想知道暴力函数,因为它们可以轻松地递归调用自己一百万次。)
比如这样:

function BruteForce(chars, min, max, prefix, stage) {
    for (var i = 0, len = chars.length; i < len; i++) {
        if (stage >= min-1)
            console.log(prefix + chars[i])
        if (stage < max-1)
            BruteForce(chars, min, max, prefix + chars[i], stage+1)
   }
}

BruteForce("abc", 1, 5, "", 0)

(现在考虑这个伪代码,因为我的问题不仅仅是关于 JavaScript。)

这样的函数不会用新参数填充堆栈直到溢出吗?

如果您在 C/C++ 等中执行类似的操作会发生什么?
调用约定重要吗?不同的调用约定能够如何处理这个问题?

为什么上面的 JavaScript 代码不会导致堆栈溢出?

【问题讨论】:

  • 如果不小心,递归可能总是导致堆栈溢出(在使用堆栈进行函数调用的语言中)。
  • 调用约定很重要是什么意思?
  • @therainmaker 我的意思是使用不同的调用约定会有什么不同。那么在堆栈溢出方面,一些调用约定会比其他调用约定做得更好吗?
  • 我可以问一下,为什么是关闭/关闭投票?

标签: javascript c recursion stack calling-convention


【解决方案1】:

让我们尝试一一回答您的问题:

这样的函数不会用新参数填充堆栈直到溢出吗?

在某些情况下是的。这取决于参数本身以及堆栈的处理方式,这非常依赖于实现。

如果你在 C/C++ 等中执行类似的操作会发生什么?

一般来说,C/C++ 没有提供太多的“堆栈处理”方式。堆栈溢出是未定义的行为。

在嵌入式系统上,您基本上会为堆栈分配一些内存,如果您溢出它...好吧,您会继续写入内存的其他部分(堆、静态数据...)。最终,您将耗尽可用内存,然后会发生什么取决于 CPU 和您的运行时 - 崩溃、停顿或诸如此类的事情。

在托管系统(Windows、POSIX...)上,堆栈溢出可能会涉及页面错误异常,这可能会提示运行时分配新页面并将它们添加到堆栈中(如果可能)。但是,只有当你的运行时这样工作时,它才可能表现得像一个嵌入式系统,或者根本不处理页面错误(因此你的程序会崩溃)。

调用约定重要吗?不同的调用约定能在多大程度上处理这个问题?

不多,因为这几乎必须使用将参数放入堆栈的调用约定。哪个是第一个,哪个是最后一个并不重要(你也没有在函数名前加下划线)。递归函数将忽略“注册”调用约定(“快速调用”或任何编译器调用的约定),或者只是将参数放入堆栈,然后复制到寄存器,然后函数将启动。

即使在滑动寄存器窗口 CPU(如 SPARC)上,也没关系,因为寄存器窗口大多是浅的,您将再次回到使用堆栈。

为什么上面的 JavaScript 代码不会导致堆栈溢出?

AFAICT,上面的代码没有很深的堆栈“嵌套”(你的“嵌套”高达max,它是5),因为Javascript中几乎所有东西都是int,double或指针(来自低级内存 POV),您的五个参数不会占用太多内存(可能约为 40 字节)。现在,如果您要将max 增加到2^31 之类的值,我猜可能会有麻烦。 :)

另外,请记住,Javascript 解释器可能会做一些奇怪的事情,比如弄清楚你永远不会更改 charsminmax,并且不要每次都将它们放在堆栈上(这样可以节省空间)。理论上,即使是 C/C++ 编译器也可以这样做,但只是作为“整个程序优化”的一部分。

【讨论】:

  • 惊人的答案!非常感谢!
【解决方案2】:

JavaScript 代码不会导致堆栈溢出。作为垃圾收集语言,所有内容都放在堆上。现在有可能最终堆会撞到堆栈,并导致问题。但是现在堆可能太大了,以至于它可能永远不会发生,不像堆栈总是有限的。

【讨论】:

    【解决方案3】:

    What would happen if you execute something like that in C/C++ and the like? 你只会得到堆栈溢出。终端将输出与之相关的错误消息。注意程序会正确执行到一个点,然后会抛出堆栈溢出错误,执行会突然终止。

    Why doesn't the Javascript code cause a stack overflow? 我之前没有 javascript 的经验,但原因似乎很简单,堆栈还没有溢出。如果你有足够深的递归,堆栈应该溢出。为了测试这一点,为什么不将start 初始化为0 并将max 设置为10^9。递归可能不会导致堆栈溢出的一种情况是,如果语言不使用堆栈进行函数递归。

    【讨论】:

    • 如果我将编译器设置为使用不同的调用约定会怎样?这会有什么不同?
    猜你喜欢
    • 2018-10-29
    • 1970-01-01
    • 2011-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多