如果我想要一个全局 VLA,我可以在 main 函数中使用 alloca() 吗?

问题描述

我的应用程序有一个 main 函数,我分配了例如配置文件的路径等。目前我为它们使用 malloc,但它们永远不会被释放并且始终可用于在应用程序的整个生命周期中使用。我什至从未释放它们,因为操作系统已经在应用程序终止时自动回收分配的内存。此时,是否有任何理由不使用 alloca 而不是 malloc,因为程序在 main 返回时结束,并且 alloca 内存仅在分配给它的函数被释放后才被删除.因此,基于此逻辑,只有在程序结束时才释放在主函数中使用 alloca 分配的内存。这些陈述是否正确,是否有任何理由不使用 allocaalloca is bad practice 所以当我说 alloca 意味着 alloca main 中制作 VLA) main 表示持续到程序终止的“全局 VLA”类对象?

解决方法

你可以在 main 中使用 alloca/VLA,但为什么呢?

使用它们的典型原因是如果您有一些被调用很多的性能敏感部分,并且您不想要 malloc/free 的开销。对于main,你的数据在程序开始时分配一次,所以几次malloc调用的开销可以忽略不计。

在 main 中不使用 alloca/VLA 的另一个原因是它们消耗堆栈空间,与堆空间相比,这是非常有限的资源。

,

取决于您需要多少内存。如果它足够小(比如几百字节左右),您可以安全地在 alloca 中执行 main() 或使用 VLA。

但是,如果这些数组的大小有一个不是很大的已知上限,那么以该上限为大小在全局范围内声明它们会更好也更安全。这样您就不会消耗堆栈空间,也不必malloc 然后确保分配成功。阅读的人也很清楚,这段内存与程序一样长。

如果尺寸可以任意大,那么最好的做法是继续使用 malloc(),就像您已经使用的那样。顺便说一句,即使您在 malloc() 中调用 main() 并在程序的整个生命周期中使用它,在退出之前释放它仍然被认为是一种很好的做法。

,

技术上不会,因为在函数中声明的任何变量都不是全局变量。但是你可以这样做:

char *buffer;

int main(void) {
    char buf[size];
    buffer = buf;

这将为您提供一个全局访问缓冲区的接口。

此时,有什么理由不使用 alloca 而不是 malloc

这是一个通常应该反过来问的问题。是否有任何理由使用 alloca 而不是 malloc?如果您遇到性能问题,请考虑更改,但如果您只想避免使用 free,我认为这是一个糟糕的理由。

但我真的不明白这里的重点。如果你有一个已分配的缓冲区,你想从程序开始到结束,然后在主函数的末尾释放它。

int main(void) {
    char *buf = malloc(size);
    // Do work
    free(buf);
}

我写了一篇关于 alloca 和 VLA:s 的长篇回答,您可能会觉得有用。 Do I really need malloc?

,

VLA(由标准定义)和非标准 alloca 都旨在用于在本地范围内分配临时小型数组。没有别的。

在堆栈上分配大对象是一个众所周知的微妙和严重堆栈溢出错误的来源。这就是您应该避免大型 VLA 和 alloca 对象的原因。每当您需要文件范围内的大对象时,它们应该是 static 数组或使用 malloc 动态分配。

需要注意的是,堆栈分配通常比堆分配要快,因为堆栈分配本身不需要关心查找、碎片和其他特定于堆实现的问题。堆栈分配只是说“这 100 个字节是我的”,然后你就可以开始了。

关于“堆栈与堆”的一般混淆,请参阅What gets allocated on the stack and the heap?

你甚至不能在文件范围内放置一个标准的 VLA,因为那里的数组大小需要是一个整数常量表达式。加上标准 (C17 6.7.6) 明确规定您不得:

如果一个标识符被声明为一个带有静态或线程存储的对象 持续时间,它不应具有可变长度的数组类型。

至于 alloca 它不是标准 C 并且因此很糟糕。但它也很糟糕,因为它没有任何类型安全,所以 VLA 比 alloca 更受欢迎 - 它更安全,更便携。

应该注意的是,VLA 在现代编程中的主要目的是启用指向 VLA 的指针,而不是分配 VLA 类型的数组对象,这是一个用途有限的特性。


我什至从未释放它们,因为操作系统已经在应用程序终止时自动回收分配的内存。

虽然这是正确的,但手动调用 free() 仍然被认为是一种很好的做法。因为如果您在程序中的某处有任何堆损坏或与指针相关的错误,您将在调用 free() 时崩溃。这是一件好事,因为它可以让您在开发过程中尽早发现此类(常见)错误。

(如果您担心 free() 的性能,您可以从发布版本中排除 free() 调用,只在调试版本中使用它们。虽然关闭程序时性能很少成为问题 - 通常您如果有的话,可以关闭 GUI,然后让程序在后台咀嚼清理代码。)