C ++如何在堆栈上动态分配内存?
Posted
技术标签:
【中文标题】C ++如何在堆栈上动态分配内存?【英文标题】:C++ How to allocate memory dynamically on stack? 【发布时间】:2011-09-14 04:03:45 【问题描述】:有没有办法在stack而不是堆上分配内存?我找不到这方面的好书,这里有人有想法吗?
【问题讨论】:
如果您想控制 std::string 或 std::vector 分配内存的位置,请检查以下问题:***.com/questions/354442/… @Neil:快速分配/释放临时工作空间? @Neil:如果函数需要少量但可变的临时对象?您可以使用vector
,但如果在紧密循环中调用该函数,那么如果可以快速分配和释放内存而不用担心碎片,那就太棒了。
@Andre - 然后在堆栈上分配少量但固定的对象。如果我们不知道上限,alloca 无论如何都会崩溃。
@Alf 我从 2.0 开始就一直在为 Windows 编程,但从未使用过,也从未见过。,
【参考方案1】:
使用alloca()
(有时称为_alloca()
或_malloca()
),但be very careful about it——它会在你离开一个函数时释放它的内存,而不是当你超出范围时,所以你会很快崩溃,如果你在循环中使用它。
例如,如果您有类似的功能
int foo( int nDataSize, int iterations )
for ( int i = 0; i < iterations ; ++i )
char *bytes = alloca( nDataSize );
// the memory above IS NOT FREED when we pass the brace below!
return 0;
// alloca() memory only gets freed here
然后alloca() 将分配一个额外的 nDataSize 字节每次通过循环。在您从函数返回之前,不会释放 alloca() 字节。因此,如果您的 nDataSize
为 1024,iterations
为 8,您将在返回前分配 8 KB。如果你有一个 nDataSize
= 65536 和 iterations
= 32768,你将分配总共 65536×32768=2,147,483,648 字节,几乎肯定会炸毁你的堆栈并导致崩溃。
轶事:如果你写超过缓冲区的末尾,你很容易遇到麻烦,特别是如果你将缓冲区传递给另一个函数,并且该子函数对缓冲区的长度有错误的想法。 I once fixed a rather amusing bug 我们使用 alloca()
创建临时存储空间,用于渲染 TrueType 字体字形,然后将其发送到 GPU 内存。我们的字体库在计算字形大小时没有考虑瑞典 Å 字符中的变音符号,所以它告诉我们在渲染之前分配 n 个字节来存储字形,然后实际渲染 n +128 字节。额外的 128 字节写入调用堆栈,覆盖了返回地址并引发了非常痛苦的非确定性崩溃!
【讨论】:
@Neil Butterworth 我们实际上不在嵌入式开发中使用标准 C++ 的众多原因之一。 =P 如果我在递归函数中使用它会怎样?有没有什么副作用?我也不太明白最后一部分,“......如果你在循环中使用它就会爆炸”。但是循环仍然在函数中,对吧? @Mark:如果你在循环中执行 alloca,它会在每次迭代中分配更多内存,但在你从函数返回之前不会释放内存(而像std::vector
local 这样的容器到循环将在循环范围结束时被释放)。
@Mark,你可以在递归循环中使用它。在正常循环中,它不会在函数返回之前释放,因此它可能会产生堆栈溢出,但在递归循环中,它将在每次迭代中被清除。
@Mark :好吧,停下来好好想想。 alloca() 是做什么的,函数的返回类型如何影响它?【参考方案2】:
由于这是标记为 C++,通常您只需在正确的范围内声明所需的对象。它们在堆栈上分配,并保证在范围退出时被释放。这是RAII,是 C++ 优于 C 的一个关键优势。不需要malloc
s 或new
s,尤其是不需要alloca
s。
【讨论】:
这里的问题是很多C++对象为自己分配内存。 @Zan Lynx - 果然如此。但是,在什么情况下,您会在堆栈上分配这样的对象图? 您可以调用填充向量的函数。你可能需要一个字符串。您可能需要这些东西非常快速且线程安全。如果您不需要这些东西来超越功能,那么堆栈存储就是正确的地方。 @Zan - 对于专门的应用程序,我可以看到这一点。我认为,如果 OP 澄清了为什么需要这样做,我会更容易说服。在大多数情况下,我的观点是不需要。 @Steve - 我主要在嵌入式环境中工作,而且我对堆分配内存的使用经常受到很大限制。【参考方案3】:您可以声明一个本地 char[1024]
或任何您想要的字节数(最多),然后获取本地地址作为指向堆栈上此内存块的指针。不完全是动态的,但如果需要,您可以使用自己的内存管理器来包装此内存。
【讨论】:
如果不是公认的答案,这应该是该特定问题的第二好的答案。与 _alloca 答案不同,它干净简单。 @MarkusL.Clean 简单且错误,因为该问题专门询问动态(非静态)堆栈分配。【参考方案4】:见_malloca
。
【讨论】:
【参考方案5】:Article discussing about dynamic memory allocation
我们可以通过以下方式在堆栈内存上动态分配可变长度空间 使用功能 _alloca。该函数从程序堆栈中分配内存。它只需要分配字节数并将 void* 返回给 分配空间就像 malloc 调用一样。这个分配的内存将是 在函数退出时自动释放。
所以它不需要被显式释放。一个必须记住 这里分配大小,因为可能会发生堆栈溢出异常。堆 溢出异常处理可用于此类调用。的情况下 堆栈溢出异常可以使用
_resetstkoflw()
来恢复 返回。所以我们带有
_alloca
的新代码将是:int NewFunctionA() char* pszLineBuffer = (char*) _alloca(1024*sizeof(char)); ….. // Program logic …. //no need to free szLineBuffer return 1;
【讨论】:
欢迎来到 SO!考虑在答案中引用最重要的部分,因为链接可能会失效。 嗨,欢迎来到 SO!通常,仅指向另一个答案的答案不被视为答案。请参阅meta.stackexchange.com/a/118694 了解详情;-) 虽然 alloca 是一种实现方式,但也有一些缺点使其成为不好的做法。请参考这里的讨论; ***.com/a/1018865/83005【参考方案6】:当/如果 C++ 允许对数组边界使用(非静态)const
值,它会更容易。
目前,我所知道的最好的方法是通过递归。有各种巧妙的技巧可以做到,但我所知道的最简单的方法是让您的例程声明一个固定大小的数组,然后填充并操作它所拥有的。完成后,如果需要更多空间来完成,它会调用自身。
【讨论】:
你回答了什么问题? 说什么? C++确实允许数组边界的常量值(如果你的意思是数组大小)。 Neil Butterworth - 哈哈,是吗?我是在 VC6 上学习的,所以偶尔会遇到一个不真实的“事实”。因此,您可以例如:将文件中的“数组大小”读入size_t const
,然后将其用作数组索引大小?
@T.E.D.:不,你不能。如果认为 Neil 理解 static const
你的意思是本地的 const
就像在 void f(const int n) ...
中一样。
@André Caron - 没错。我也不会称其为“显而易见的”。 Ada 允许您毫无顾忌地做这件事。也许在 C++ 中允许这样做是有害的,但从我的角度来看,这似乎是该语言的任意限制。在更改之前,我知道的唯一标准解决方法是将问题模块化并使用递归。【参考方案7】:
您可以使用BDE C++ 库,例如
const int BUFFER_SIZE = 1024;
char buffer[BUFFER_SIZE];
bdlma::BufferedSequentialAllocator allocator(buffer, BUFFER_SIZE);
bsl::vector<int> dataVector(&allocator);
dataVector.resize(50);
BDE 提供全面的分配器选项以及 bsl::vector 等集合,可以使用多态分配器而无需更改容器的类型。
您也可以考虑:
https://github.com/facebook/folly/blob/master/folly/docs/small_vector.md http://www.boost.org/doc/libs/1_55_0/doc/html/container/non_standard_containers.html#container.non_standard_containers.static_vector http://llvm.org/docs/doxygen/html/classllvm_1_1SmallVector.html【讨论】:
以上是关于C ++如何在堆栈上动态分配内存?的主要内容,如果未能解决你的问题,请参考以下文章