所以我们假设我们有一个递归的暴力函数
(我特别想知道蛮力功能,因为它们可以很容易地递归地称自己为百万次。)
像这样,例如:
{{1}}
(现在考虑这个伪代码,因为我的问题不仅仅是关于JavaScript。)
这样的函数在溢出之前不会用新参数填充堆栈吗?
如果您在C / C ++等中执行类似的操作会发生什么? 呼叫惯例是否重要?不同的调用约定能够处理这个问题的程度如何?
为什么上面的JavaScript代码不会导致堆栈溢出呢?
答案 0 :(得分:1)
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
。递归可能不会导致堆栈溢出的一种情况是,如果语言没有使用堆栈进行函数递归。
答案 1 :(得分:1)
JavaScript代码不会导致堆栈溢出。作为垃圾收集语言,一切都放在堆上。现在有可能最终堆会到达堆栈,并导致问题。但是现在堆可能是如此之大,以至于它可能永远不会发生,不像堆栈总是有限的。
答案 2 :(得分:1)
让我们一个接一个地回答你的问题:
这样的函数在溢出之前不会用新参数填充堆栈吗?
在某些情况下是的。它取决于参数本身以及如何处理堆栈,这与实现有关。
如果你在C / C ++等中执行类似的操作会怎么样?
通常,C / C ++在“堆栈处理”方面没有提供太多帮助。堆栈溢出是未定义的行为。
在嵌入式系统上,你基本上会给堆栈分配一些内存,如果你溢出它......好吧,你继续写一些内存的其他部分(堆,静态数据......)。最终,你将耗尽可用内存,然后发生的事情取决于CPU和运行时 - 崩溃,失速或某些事情。
在托管系统(Windows,POSIX ...)上,堆栈溢出可能涉及页面错误异常,如果可能,可能会提示运行时分配新页面并将其添加到堆栈中。但是,只有当您的运行时工作时,它可能表现得像嵌入式系统,或者只是不处理页面错误(因此您的程序将崩溃)。
呼叫惯例是否重要?不同的调用约定能够处理这个问题有多好?
并不多,因为这几乎必须使用将参数放在堆栈上的调用约定。哪个是第一个,哪个是最后一个并不重要(在函数名称之前也没有下划线)。对于递归函数,“寄存器”调用约定(“快速调用”或编译器调用它)将被忽略,或者只是将参数放在堆栈上,然后复制到寄存器然后函数将启动。
即使在滑动寄存器窗口CPU(如SPARC)上也没关系,因为寄存器窗口大多是浅的,你将再次回到使用堆栈。
为什么上面的JavaScript代码不会导致堆栈溢出呢?
AFAICT,上面的代码没有非常深的堆栈“嵌套”(你“嵌套”高到max
并且它是5
),因为Javascript中的任何东西都是一个int,double或指针(来自低级内存POV),你的五个参数不会占用太多内存(可能是~40个字节)。现在,如果您要将max
增加到类似2^31
的内容,我猜可能会遇到麻烦。 :)
另外,请记住,Javascript解释器可能会做一些时髦的事情,比如你永远不会改变chars
,min
或max
而只是不把它们放在每次堆叠(从而节省空间)。从理论上讲,即使是C / C ++编译器也可以这样做,但仅作为“整个程序优化”的一部分。