在递归函数中,堆上分配vs堆栈上分配



当我定义一个递归函数时,在堆上分配局部变量,然后在函数返回之前清理它们,比在堆栈上分配它们更好/更安全吗?嵌入式系统的堆栈大小非常有限,当递归运行得太深时,存在堆栈溢出的危险。

答案取决于您的应用程序领域和平台的具体情况。"嵌入式系统"是一个模糊的术语。一个运行微软Windows或Linux的大示波器是频谱的一端。它的大部分可以像普通的个人电脑一样编程。它们不应该失败,但如果失败了,只需重新启动它们。

在频谱的另一端是安全关键领域的控制器。想想西门子的这些开关,它们在任何情况下都必须在毫秒内做出反应。失败不是一个选项。它们可能不运行Windows。可用资源是有限的。这里的编程规则和程序大不相同。

现在让我们检查一下您拥有的选项,递归(或不递归!)与动态或自动内存分配。

性能方面,堆栈更快,所以它是首选。动态分配还涉及到一些用于簿记的内存开销,如果数据单元很小,则内存开销很大。自动内存可能会出现内存碎片问题(尽管导致碎片的场景——对象的不同生命周期——可能无法在没有动态分配的情况下直接解决)。

但是在某些系统上,堆栈大小确实比堆大小小得多;您必须了解系统上的内存布局。示波器将有大量的内存和大堆栈;电源开关不打开。

如果你担心内存不足,我会遵循Christian的建议,完全避免递归,而是使用循环。迭代可能只是使内存使用保持不变。此外,递归总是使用堆栈空间,例如用于返回地址和-value。动态分配"局部"变量的想法只对较大的数据结构有意义,因为您仍然需要将指向数据的指针作为自动变量保存,这也会占用堆栈上的空间(并且会增加总体内存占用)。

一般来说,在资源有限的系统上,限制程序使用的最大资源是很重要的。时间也是一种资源;动态分配使得实时几乎不可能实现。

应用程序域规定了安全需求。在安全关键领域(起搏器!),程序绝对不能失败。这个理想是不可能实现的,除非这个程序是微不足道的,但人们付出了巨大的努力来接近它。在其他领域,程序可能在它无法处理的情况下以特定的方式失败,但它不能以静默或未定义的方式失败(例如,通过覆盖数据)。例如,与其动态分配未知数量的数据,不如使用预定义的固定大小的数据数组,并使用数组中的元素,并进行绑定检查。

当我定义一个递归函数时,在堆上分配局部变量,然后在函数返回之前清理它们,比在堆栈上分配它们更好/更安全吗?

你有C和c++标签。这是一个对两者都有效的问题,但我只能评论c++。

在c++中,最好使用堆,尽管它的效率略低。这是因为如果堆内存用完,new可能会失败。在失败的情况下,new将抛出异常。然而,耗尽堆栈空间并不会导致异常,这也是alloca在c++中不受欢迎的原因之一。

我认为一般来说,您应该在嵌入式系统中完全避免递归,因为每次调用函数时,返回地址都被推入堆栈。这可能导致意外溢出。试试切换到循环。

回到你的问题,mallocing会更慢,但更安全。如果没有堆空间,那么malloc将返回一个错误,您可以安全地清理内存。这需要付出很大的速度代价,因为malloc可能相当慢。

如果您提前知道预期的迭代次数,则可以选择对需要的变量数组进行错置。这样,您只需要malloc一次,这样可以节省时间,并且不会冒意外填满堆或堆栈的风险。此外,您将只剩下一个变量可以释放。

最新更新