应该避免在C/ c++中使用递归调用吗?

Should one avoid using recursive call in C/C++?

本文关键字:递归 调用 c++      更新时间:2023-10-16

应该避免在C/c++中使用递归函数调用吗?

我的工作是机器学习/数据挖掘,所以让我的代码可扩展对我来说非常重要。

当我使用Java时,我尽量避免使用递归调用,因为我经常使调用堆栈溢出。虽然有一些选项可以控制分配给调用堆栈的内存量,但我认为让我的程序依赖于更少的参数是更可取的。因此,当清楚如何实现不递归调用时,可能使用我自己管理的堆栈,我就这样做了。但是,即使在Java中,我也不确定这是否是一个正确的学科。

据我所知,在C/c++中没有调用堆栈,所以我不会担心溢出它。因此,我很好奇:在程序的可伸缩性方面,是否会尝试避免使用递归,或者鼓励使用递归,或者它是特定于问题的?

这个问题没有唯一的正确答案。对于某些问题,递归工作得非常好。

据我所知,在C/c++中没有调用堆栈

澄清一下,这是不正确的:在我所知道的所有C和c++实现中,一个调用堆栈。

据我所知,在C/c++中没有调用堆栈,所以我不会担心关于溢出

啊?当然,标准没有讨论调用堆栈,但确实在大多数(如果不是全部)实现中都有调用堆栈。

现在,应该避免递归吗?首先,众所周知,每个递归函数都可以迭代地重写(即不需要递归)。而且,有时候迭代解比递归解快。但是对于某些任务,例如图中的DFS,递归是如此简单和有用,以至于您不应该避免使用它,除非您有很好的理由不使用它。相同DFS的迭代解决方案几乎同样简单,但需要更多的输入…

C &c++在一个方面有点像c++ 中的虚函数表——据我所知,在标准中没有提到它,也就是说,实现不是强制的,但出于所有实际目的,它几乎总是这样实现的。

当然有一些深奥的体系结构,特别是在嵌入式世界中,可能会避开堆栈。PIC是堆栈不友好架构的一个例子,一些微型微控制器不支持堆栈,至少不直接支持堆栈。在某些情况下,堆栈的使用是可以消除的(内联函数或对函数调用的激进优化,使用局部变量寄存器而不是堆栈空间)。

不管怎样,对于你的问题…这是一个非常主观的问题,通常可以归结为一个问题:你买得起吗?

如果你有足够的堆栈空间,并且递归不会深到破坏堆栈,递归通常是正确的选择。不总是,但经常。

但是你必须了解你的架构,你的平台,你的工具集等等,并且知道你的堆栈有多大。例如,对于许多多任务嵌入式系统,堆栈大小是在创建线程/任务时确定的,它不会"按需增长"。其他简单的(嵌入式)系统只使用单个堆栈,或者一个"后台"堆栈和一个中断堆栈。还有一些允许堆栈根据需要在限制内增长,这取决于处理器。操作系统。

如果您有Java背景,对于这种讨论对您来说是新的,我并不感到惊讶。

调用栈经常溢出

那就是你的错了。

如果使用正确,递归是一个非常有用的结构。特别是当您需要处理一组可变大小的数据结构时。限制递归是微不足道的:
int recurse(int maxlimit, ...)
{
   if (!--maxlimit) return false; // and throw an exception or something
   ...
   return completed ? finalvalue : recurse(maxlimit, ...);
}

递归可以是一种非常优雅的表达算法的方式。C/c++的主要缺点是可能发生堆栈溢出。

下面是C/c++的经验法则:

如果递归不是尾部调用,那么深度需要是数据大小的次线性

例如,如果你要递归地对二叉树进行DFS,那么该树必须是平衡的,这样递归深度为O(log n)。这确保不会出现堆栈溢出。不要使用非尾递归来遍历链表(O(n)深度)。