堆、栈和bss区的比较

5

在存储运行时数据(如缓冲区)时,哪种方式更好:堆(malloc)、栈(例如函数内的char buf[BUFSIZE])还是bss段(全局区域中的char buf[BUFSIZE])?


1
你是在写编译器吗?还是只是在查询与你正在编写的软件相关的问题? - Dipan Mehta
1
如果您正在编写自己的C/C++或JAVA程序,您并不完全可以自行做出这样的选择。(除非您正在编写自己的汇编部分)。编译器会自行完成此操作。那么真正的问题是什么?您是在问“如何强制编译器将数据放入相应的部分?”还是“编译器是否应该进行特定的放置?” - Dipan Mehta
3
据我所知,C编译器不会自己选择数据的存放位置。全局未初始化区域是bss段,局部(非静态)区域在栈上,堆由malloc分配(在C99中也可以使用可变长度数组)。是这样吗? - user1150105
坦白说,我认为数据= BSS + Heap 是由编译器选择的;软件并不在那里做出真正的选择!好吧,我被维基百科页面(http://en.wikipedia.org/wiki/Data_segment)搞混了,在那里它说“数据=数据+ BSS + HEAP”,这对我来说并没有完全意义。你应该说明除了默认情况下不在BSS或HEAP或Stack中的内容之外,其他哪些“数据”属于此类别?程序员需要做出选择吗? - Dipan Mehta
@DipanMehta,ELF文件中的(data + bss + heap)构成了进程的数据段,该ELF将被映射到其中。 - 4pie0
3个回答

6
这取决于您想要用缓冲区做什么。
通常应避免使用全局变量,您必须非常小心,以避免出现与它们相关的问题。
如果缓冲区仅在一个函数中需要,并且BUFSIZE不太大(最多几KB),则可以将其设为本地变量。但是,请注意总堆栈使用情况,因为嵌套调用会增加堆栈使用情况。静态局部变量仅在您想要保留调用之间的值时才需要,从而有效地使其成为具有本地作用域的全局变量。但是,当您进行多线程或想要使用递归时,这会让您头疼。
如果缓冲区跨越函数调用使用,或者BUFSIZE相当大,则使用malloc() / free()。如果该函数经常被调用,则将其分配一次到函数外部可能是个好主意,执行所有函数调用,然后释放它,而不是为每个函数调用分配和释放另一个缓冲区。但是,这接近过度优化,因为它不必要地将函数的内部结构与外部调用者耦合起来。
在更大的视角下,当您的程序增长时,您希望为其提供更多的结构并清晰地定义责任,尤其是对于内存处理,否则您最终将失去追踪。该缓冲区是具有特定任务的特定模块的工作细节。一种典型的方法是使用结构体以OOP方式组织数据,并使用create和destroy函数分配和释放此类对象。然后,该缓冲区可以成为此结构体的一部分。
struct s_foo
{
    char buf[BUFSIZE];
    ...
};

struct s_foo *foo_create (...);
void foo_destroy (struct s_foo *foo);
void foo_action (struct s_foo *foo);

这使您可以同时拥有任意数量的foos,每个都有自己的缓冲区,彼此独立。此外,在不使用静态变量的情况下,缓冲区内容在调用之间保持不变。


0
你需要一个在加载时分配并在程序生命周期内保持不变的静态变量。所以选择三,也就是所谓的“bss”部分。

-1

显然,由于大小受限,堆是不可行的。我也不会选择静态变量,因为它们不可重入并且在递归中会带来混乱。所以,将其放在堆栈帧中。

void f(...) {
    char buf[BUFSIZE];
}

顺便说一下,除非绝对必要,否则最好避免使用静态或全局变量。


如果 BUFSIZE 太大怎么办? - user1150105
@user14284,由于大小限制,堆栈帧已经超出了范围。但是,它仍然不是静态的,因为其生命周期是程序生命周期,而big-buf将占用大量内存。这只留下了malloc。现在malloc是一个野兽,所以必须正确地处理它。 - theD

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