防止用户可编程计算器中的堆栈溢出/无限递归
我编写了一个用户可编程计算器,现在遇到了以下问题。
假设用户在其中写入以下内容:
fun(n) = fun(n-1)
然后尝试调用 fun(42)
(或任何数字)。现在显然,用户的代码会导致无限递归,进而导致整个计算器因堆栈溢出而崩溃。
如何让计算器优雅地退出计算?
我已经研究过 Mathematica 如何处理类似的情况,它只是说
$IterationLimit::itlim: Iteration limit of 4096 exceeded.
我尝试了类似的方法,但是,由于堆栈大小取决于操作系统,我如何知道使用什么数字作为迭代限制(是的,我通过实验发现了一个“似乎有效”的数字,但感觉不对)?
感谢您抽出时间。计算器的核心是用C语言编写的。
I wrote a user-programmable calculator, and now I'm hitting the following problem.
Let's say a user writes the following in it:
fun(n) = fun(n-1)
and then tries to call fun(42)
(or whatever number). Now obviously, the user's code will cause infinite recursion, which in turn causes the whole calculator to crash with a stack overflow.
How could I make the calculator quit the calculation gracefully?
I have looked at how Mathematica handles similar situations, and it just bails out saying
$IterationLimit::itlim: Iteration limit of 4096 exceeded.
I have tried a similar approach, but then, as the stack size is OS-dependent, how do I know what number to use as the iteration limit (yes, I found a number that "seems to work" by experimentation, but that doesn't feel right)?
Thanks for your time. The core of the calculator is written in C.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这只是 C 语言中丑陋的小角落之一——没有可移植的方法来知道可以安全使用多少堆栈。从技术上讲,甚至不需要足够的堆栈来从
main()
调用单个函数 - 这被认为是一个实现质量问题。最安全的方法是展开递归,以便它使用您通过
malloc()
分配的自己的表达式堆栈,而不是无限制的真正递归。这样,如果在达到应用程序定义的递归限制之前malloc()
返回NULL
,您就可以摆脱困境。This is simply one of the ugly little corners of C - there's just no portable way to know how much stack you can safely use. Technically, there doesn't even have to be enough stack to call a single function from
main()
- it's considered a quality-of-implementation issue.The safest approach is to unroll your recursion so that it uses its own expression stack that you allocate with
malloc()
, rather than unbounded genuine recursion. That way you can bail out ifmalloc()
returnsNULL
before you reach your application-defined recursion limit.不要将用户级数学函数调用实现为 C 函数调用,而是使用 malloc 实现您自己的基于堆的堆栈。这将为您提供更多级别的递归以及可靠地判断失败的方法。
或者,您可以对递归级别设置一个相当小的限制。
Instead of implementing user-level math function calls as C function calls, implement your own heap-based stack with malloc. This will give you a lot more levels of recursion and a way to tell reliably for failure.
Alternatively you could just put a reasonably small limit on levels of recursion.
一个快速的技巧是只跟踪递归深度,如果超过则退出。一个计数器就足够了。当然,您可能需要在每次评估之间重置计数器 - 但它不如运行您自己的堆栈那么强大。
A quick hack is to just keep track of the recursion depth and exit if it is exceeded. A counter will suffice. You may need to reset the counter between each evaluation of course - but it is not as robust as running your own stack.