图 3
对于堆我们已经知道了必须调用相应的API来分配内存,那从栈空间分配内存也需要调用API吗?答案是通常不需要,为什么是通常?因为,在有的平台上(Linux上就是)提供栈空间的分配API,即这种API被调用时,是从调用任务的栈空间中分配内存的。对于这一功能,在嵌入式系统中使用得非常的少,我也不建议大家使用。对于下面的代码,mem_main、mem_foo和mem_bar的大小是4K字节(假设int类型的大小是4字节),这些内存就是自动(注意是自动)分配在运行任务的栈上的。我们假设某个任务当前所使用的栈是零字节,当这一任务运行到main中且没有进入foo ()时,其所占用的空间大小是大约4K字节,之所以用大约这个词,是因为函数的调用还有其它的栈开销。一旦任务运行进入foo ()函数但没有进入bar ()函数,那么所占用的栈的大小就变为大约8K字节。同样的,如果程序运行进入bar ()函数,那么所占用的栈空间大约就是12K字节了。
00001:voidbar()
00002:{
00003:intmem_bar[1024];
00004:// application logic
00005:}
00006:
00007:voidfoo()
00008:{
00009:intmem_foo[1024];
00010:bar();
00011:}
00012:
00013:intmain()
00014:{
00015:intmem_main[1024];
00016:foo();
00017:return 0;
00018:}
如果程序继续运行,从bar ()函数返回到foo ()函数中,那么其所占用的栈空间就从大约12K字节变成了大约8K字节了。相类似的是,如果程序从foo ()函数中返回到main ()函数,那么所占用的栈空间又变为大约4K字节了。对于嵌入式系统开发,由于任务栈通常都比较的小,那这告诉我们什么呢?我想有以下几点需要注意。
1)函数的调用深度越是深,由于每一级的函数通常都会有局部变量,那么所使用的栈空间也会累积得越大。
2)递归调用需要的栈空间会相对的大(视具体的情况),在嵌入式系统中也建议少用。
3)我们应当尽可能的不要在函数中定义占用内存空间较大的局部变量。
下面,我们总结一下堆与栈的区别,它们是:
1)堆是大家共享的。任务可以通过调用API来从堆中分配内存空间。
2)栈是任务所独有的。在嵌入式系统中,当一个任务创建起来后其栈空间的大小往往是定了的。函数中的局部变量是由编程语言自动从栈上分配的,我们不需要调用API进行空间分配。
最后我有一个问题留给读者您,这个问题是:
前面的讲解中,我们说任务的栈是由操作系统的任务创建API从堆中分配出来的,那栈是否也可以位于.data段或是.bss段中呢?为什么?
答案
由于堆从本质上说来就是一块内存,由于在C语言中一块内存可以从堆中分配,也可以从.data段或是.bss段中分配。因此,任务的栈也是可以从这三块内存中分配获得,也就是说最终的答案是:可以。
你或许还想看一看《程序中的段》。
如果你觉得本文的哪些地方需要改进或是存在一些不明白的地方,请留言。如果你想参与讨论嵌入式系统开发相关的话题,请加入技术圈(g.51cto.com/UltraEmbedded)。