在啥情况下 alloca() 有用?
Posted
技术标签:
【中文标题】在啥情况下 alloca() 有用?【英文标题】:In which cases is alloca() useful?在什么情况下 alloca() 有用? 【发布时间】:2011-03-28 00:23:41 【问题描述】:当您总是可以在堆栈上分配一个足够大以适应所有用途的固定大小缓冲区时,为什么还要使用 alloca()?这不是一个反问句……
【问题讨论】:
另见***.com/questions/1018853/… 'goto' 也被认为是不好的做法,但有时需要它。是否需要 alloca()? 【参考方案1】:如果缓冲区的大小在运行时发生变化,或者您只是有时需要它,这可能很有用:与每次调用中固定大小的缓冲区相比,这将使用更少的堆栈空间。特别是如果函数位于堆栈的高位或递归。
【讨论】:
+1 是迄今为止最有说服力的答案。当函数是递归时,在堆栈上分配可能需要的最大缓冲区大小可以显着减少最大递归调用的数量。所以使用 alloca() 可以减少需要处理最坏情况的惩罚。 你能详细说明一下吗?【参考方案2】:如果无法知道编译时可能需要的最大大小,您可能想要使用它。
您是否应该是another question - 这不是标准的,也没有办法判断它是否会导致堆栈溢出。
【讨论】:
拍拍背“堆栈溢出”!【参考方案3】:从不 - 它不是 C++ 的一部分,在 C 中没有用处。但是,您不能分配“堆栈上的静态缓冲区” - 静态缓冲区是在编译时分配的,而不是在堆栈上。
alloca() 的要点当然是它不是固定大小的,它在堆栈上,并且在函数退出时会自动释放。 C++ 和 C 都有更好的机制来处理这个问题。
【讨论】:
zr 是否编辑了问题?它现在显示为“固定大小”,而不是“静态” alloca 是 C++ 的一部分,不亚于它在 C 中的一部分——两者都没有标准化。 @shog9 - 如果问题已被编辑,我们会看到它的含义。我们没有。 @Heath 已被编辑 - 在初始限制内,不会显示在编辑历史记录中。 @Heath:正如 Neil 所指出的,在发布或编辑之后有一个宽限期,其中作者/最后一个编辑所做的编辑不会生成单独的修订条目。鉴于 Neil 和 zr 都在几分钟之内发布了消息,我认为这很可能已经发生了......【参考方案4】:alloca() 在什么情况下有用?
我唯一一次看到使用 alloca 是在 Open Dynamics Engine 中。 AFAIK 他们正在用它分配巨大的矩阵(因此编译的程序可能需要 100MB 堆栈),当函数返回时自动释放这些矩阵(对我来说就像智能指针剽窃)。这是很久以前的事了。
虽然它可能比 new/malloc 快得多,但我仍然认为这是一个坏主意。 当场景变得太复杂而无法处理时,程序可能会因堆栈溢出而崩溃(即误导),而不是礼貌地用完 RAM。不是一个好的行为,IMO,特别是对于物理引擎,你可以很容易地期望有人将几千块砖扔到场景中,看看当它们同时碰撞时会发生什么。另外,您必须手动设置堆栈大小 - 即在具有更多 RAM 的系统上,程序仍将受到堆栈大小的限制。
堆栈上的固定大小缓冲区足够大以适应所有用途?这不是一个反问句……
如果您需要所有用途的固定大小缓冲区,那么您也可以将其放入静态/全局变量或使用堆内存。
【讨论】:
作为“智能指针”盗版者,我猜alloca
使用时间机器窃取了这个想法。 alloca
来自 1960 年代后期,智能指针是 1986 年后的......在其他地方已经提出了关于线程安全性的观点,它是静态/全局变量不共享的 alloca 的积极因素。堆取决于你如何使用它,所以不要用 900 个字符来解决它。
@Heath Hunnicutt:“alloca 使用了时间机器”Open Dynamics Engine 不是在 60 年代编写的。【参考方案5】:
alloca()
函数几乎不需要;出于内存分配目的,您可以在 C 中使用 malloc()
/free()
(或 C++ 中的可能性集合之一)并实现几乎相同的实际效果。这具有更好地应对较小堆栈大小的优势。
然而我已经看到[1]一个合法的(如果hacky!)使用它:用于检测Windows上潜在的堆栈溢出;如果分配(您想要访问的 slop 空间量)失败,您就退出了,但有足够的空间来优雅地恢复。它被包裹在__try
/__except
中,因此它不会崩溃,并且需要额外的汇编技巧来避免 gcc 引起的麻烦。正如我所说,一个黑客。但是一个聪明的方法是我见过的alloca()
唯一有效的用法。
但不要那样做。最好把代码写成不需要这样的游戏。
[1] 它在 Tcl 8.4 中(可能还有更早的 Tcl 版本)。它在以后的版本中被删除。后来的版本删除了它,因为它很挑剔,非常棘手且令人深感不安。 8.6 使用执行引擎的无堆栈实现,而不是那种时髦。
【讨论】:
FWIW:在 Windows 上,通常在堆栈的末尾有一个保护页,用于动态扩展它。一旦达到堆栈的限制并且这个保护页面被命中,你就会得到异常——但只有一次。除非他们在检测到堆栈结束后重置保护页,否则这个技巧只会工作一次……下一次,程序将立即被系统终止。 @Shog:很有趣,虽然我们不再需要一些东西;我们改变了实现引擎的工作方式,不再需要深度 C 堆栈。 :-) 我只是认为人们将它用作alloca 的用途会很有趣,而malloc 根本无法复制。【参考方案6】:使用alloca()
可能当您无法可靠地使用malloc()
(或C++ 中的new
,或其他内存分配器)时,或根本无法使用,但您可以假设存在您的堆栈上有更多可用空间 - 也就是说,当您真的无能为力时。
例如,在glibc
的segfault.c 中,我们有:
/* This function is called when a segmentation fault is caught. The system
is in an unstable state now. This means especially that malloc() might
not work anymore. */
static void
catch_segfault (int signal, SIGCONTEXT ctx)
void **arr;
/* ... */
/* Get the backtrace. */
arr = alloca (256 * sizeof (void *));
/* ... */
【讨论】:
以上是关于在啥情况下 alloca() 有用?的主要内容,如果未能解决你的问题,请参考以下文章
在啥情况下“Openwrt”优于“Buildroot”框架?
在啥情况下 [ [ UIApplication sharedApplication ] keyWindow ] 返回 nil?