哎,下班回家就开始大扫除,一直到凌晨才搞定,真的累了。但是计划的是今天必须将本文写完,不写完睡不着觉。那就尽快切入正题吧!
我们经常使用malloc或者new等函数或操作符来动态分配内存,这里的内存说的是堆内存,并且需要程序员手工释放分配的内存。malloc对应free,new对应delete。至于你要混着用,也不是不可以,只要确保逻辑和功能的正确性,还要在规范的限制范围内。这里我想插一句题外话,我个人觉得,只要你将一些具有相似特征的东西都摸透了,他们的差异你就会很明了,在此基础上,随便你怎么用都是成竹在胸的,只需要考虑一些外界因素就可以了,比如前面说的规范等。
本文是针对在栈上动态分配内存进行讨论,分配的内存即为栈内存,栈上的内存有一个特点即是不用我们手工去释放申请的内存。栈内存由一个栈指针来开辟和回收,栈内存是从高地址向低地址增长的,增长时,栈指针向低地址方向移动,指针的地址值也就相应的减小;回收时,栈指针向高地址方向移动,地址值也就增加。所以栈内存的开辟和回收都只是指针的加减,由此相对于分配堆内存可以获得一定的性能提升。由这些特性,也能对为什么叫“栈”内存有更进一步的理解。
我们都知道,在C99标准之前,C语言是不支持变长数组的,如果想要动态开辟栈内存以达到变长数组的功能就得依靠alloca函数。其实在gcc下,c99下的变长数组后台也是依靠alloca来动态分配栈内存的,当然这里不能完全说是调用alloca来实现的,alloca可能被优化并内联(当然你还是可以说这是在调用)。这里就不纠结这个问题了,在本文不属于重点。实际中,alloca函数是不推荐使用的,他存在很多不安全的因素,这里暂时不讨论这个问题,本文的目的是了解原理,获得认知,以至通透。
通常编译器都提供了CRT库,例如VC的诸多版本,CRT库在一些版本间差异还是比较大,新版本的CRT一般会多了很多更严格的检查和一些安全机制。本文以VS2008为例,其为alloca提供了对应的_alloca函数,编译器会将其编译为_alloca_probe_16函数,此函数位于VC_dir\VC\crt\src\intel\alloca16.asm汇编源文件中,此乃微软提供的汇编版本CRT相关函数。在此文件中,有两个版本,一个是16字节对齐的_alloca_probe_16,一个是8字节对齐的_alloca_probe_8。代码如下:
.xlist
include cruntime.inc
.list
extern _chkstk:near
; sizeofa pageofmemory
CODESEG
page
public_alloca_probe_8_alloca_probe_16 proc ; 16 byte aligned alloca
push ecx
lea ecx, [esp] + 8 ; TOS before entering this function
sub ecx, eax ; New TOS
andecx, (16 - 1) ; Distancefrom16bitalign (align down)
addeax, ecx ; Increase allocationsize
sbb ecx, ecx ; ecx = 0xFFFFFFFF if sizewrapped around
oreax, ecx ; cap allocationsizeonwraparound
pop ecx ; Restore ecx
jmp _chkstk
alloca_8: ; 8 byte aligned alloca
_alloca_probe_8 = alloca_8
push ecx
lea ecx, [esp] + 8 ; TOS before entering this function
sub ecx, eax ; New TOS
andecx, (8 - 1) ; Distancefrom8bitalign (align down)
addeax, ecx ; Increase allocationSize
sbb ecx, ecx ; ecx = 0xFFFFFFFF if sizewrapped around
oreax, ecx ; cap allocationsizeonwraparound
pop ecx ; Res