我应该避免在堆栈上放置大对象吗?

3

在非递归函数中,将大型数据类型放置在堆栈上通常是可以的吗?

其中一个方便的地方是在需要清理东西的C函数中。

例如。 我有一个C函数,允许我像这样读取/写入任意数据包。

int do_something()
{
  char buf[9000];
  struct ot_packet_t p;

  pkt_init(&p);
  pkt_set_type(&p, "WDAT");
  pkt_write_uint32(&p, some_var);
  pkt_write_data(&p, some_data, some_len);

  // other stuff...
  // if need to early exit... buf & p cleaned up.  An RAII approach.

  send_packet(buf, pkt_get_length(&p));
}

我已经给这个问题打上了C++的标签,因为它也适用于C++。 虽然至少在C++中,我通常使用auto_ptr来清理在堆上分配的较大对象。但在C语言中,我认为这不是很整洁,对吧?


1
请勿仅标记您可以想象到的每种可能适用的语言。这个通用问题适用于任何编译成本地汇编语言的语言实现,以及任何具有固定大小堆栈的平台。 - Puppy
2个回答

4
在单线程的主流PC应用程序中,则没有太多需要担心的:一点点的几KiB,再加上一点点的Kib;最终,也许会在堆栈上达到几个MiB的大小。堆栈会自动增长,对应用程序的成本非常低。
如果你正在处理一个多线程的应用程序,情况就不同了。每个线程都有自己的堆栈,并且(据我所知)该堆栈大小在线程启动时是固定的。如果你开始将线程的堆栈与大型本地变量混杂在一起,可能会消耗掉所有的堆栈空间。因此,最好使用动态内存分配来避免遇到这些问题。由于单线程应用程序也可以做动态分配,如果有合理的机会程序会演变成多线程程序,那么从一开始就使用动态分配可能更好。注意,与在堆栈上自动分配相比,动态分配会更慢。
如果你要处理的系统内存有限,则要避免创建大型对象。至少要考虑任何时候存在的对象数量,并确保尽快释放它们。

感谢您的评论。我将我的代码放入一个库中,该库将通过线程与某个东西进行交互。因此,您的评论很好。 - hookenz

1
“这取决于。” 取决于机器、设备、块的大小以及你下面堆栈的大小。
在你的例子中,一个非递归函数中的约9K块基本上都没问题。(例外情况可能是小型嵌入式设备,或者如果有很多其他函数在同一个堆栈上执行相同的操作。)
作为一种纯个人的经验法则,我通常不会在正常的应用层代码中使用超过几K的缓冲区。如果像你的例子一样进行数据包传输,并且是在一个循环中进行,你可能会看到每次动态分配都会带来性能开销,所以你可以选择将其保留在堆栈上,将静态缓冲区放在某个位置,或者使用可重复使用的缓冲区池。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接