关于C语言中Malloc()内存对齐
众所周知,在C语言中可以通过malloc()函数在堆区创建一块动态内存空间,例如:
malloc(1); //在堆区创建1个字节大小的空间
但是实际情况下malloc()创建的数据大小远超过1字节,为什么会导致这个问题呢,这就有关内存对齐问题了。
1.实验
我分别在Win10 64下和Stm32下运行了如下代码,并得到结果:


先观察运行结果,Stm32分别输出了8和16,明明我们分配的是malloc(1),应该输出1字节才对啊,怎么会变成8,更不要用说16了。
这就要引入内存对齐和malloc堆块结构的的问题了。
首先我们先看malloc()堆块结构,malloc()函数申请的堆块是由两部分组成:Header和数据区

malloc()函数创建的堆空间由两部分组成:Header和数据,这就意味着malloc()真实分配的空间大小是Header大小+数据大小。
以下代码为malloc()堆块Header结构:
typedef long Align;
union header {
struct {
union header *ptr;
unsigned
} s;
Align x;
};
可以看到Header是共同体Union,共同体所有元素共享一块内存空间,共同体所占用的空间大小是共同体中最大的元素的大小,也就是long Align x,众所周知long在64位机子占用8字节,在32位机子占用4字节。
Header长度是4字节或8字节(根据机器字长决定)
注意:我经过Stm32平台和Win10平台实验论证,Header区和数据区是一个整体,计算实际内存占用时先把数据区和Header长度相加,然后把相加后的结果作内存对齐。以32位机子的malloc(1)为例,把数据区1字节加上Header的4字节得到总共5字节,再整体作内存对齐得到8字节,实际内存占用为8字节。
再说内存对齐问题
比如此处我用Stm32申请了1个字节的堆空间,实际上他却分配了8个字节给我,按照malloc()函数分配的堆块结构,应该是1个字节的数据区+4个字节的Header区,再经过内存对齐,小于8字节的区域扩展到8字节存储,进行内存对齐后我们总共占用8字节。
经过测试,在不同的编译环境和平台下,系统默认的内存对齐系数是不一致的,32位数系统是8字节(少于8字节则以8字节存储,大于8字节则扩展8字节存储),64位系统是16字节(少于16字节则以16字节存储,大于16字节则扩展16字节存储)
Stm32经过测试,对齐系数是8字节,但是注意,经过我的实验,在Header区4字节加上数据区大小不产生溢出时(两者相加不超过最近的8的倍数),对齐系数会在8字节和16字节间交替,产生溢出时就是正常的8字节对齐,这就是为什么上面测试会出现8和16交替的情况。当malloc()申请1字节时,数据区长度1字节加上Header区长度4字节经过内存对齐后变成8字节,总长度=(数据区1字节+Header区4字节)->内存对齐=8字节
经过实验,Stm32F103C8的内存对齐系数是8字节和16字节交替的,当数据区长度加Header长度产生溢出时,内存对齐系数就是正常的8字节,当数据区长度加Header长度不产生溢出时,内存对齐系数就会交替。例如malloc(1),数据区长度是1字节,加上4后小于8不产生溢出,则内存对齐系数就会在8和16之间交替,再例如malloc(7),7+4大于8产生溢出,那么内存对齐系数就是8字节,总占用16字节。使用时需注意,这也就解释了为什么上面示例中一会输出8字节一会输出16字节了
Win10 64经过测试,对齐系数是16字节,当malloc()申请32字节时,实际我们需要的是32字节数据区空间+8字节Header空间,也就是40字节,数据区和Header区相加后内存对齐,系统会分配48字节的空间给我们存储,如下图:

系统最小malloc()分配空间大小
在64位系统,如果申请内存为1~24字节,系统内存消耗32字节,当申请25字节的内存时,系统内存消耗48字节。而对于32位系统,申请内存为1~12字节时,系统内存消耗为16字节,当申请内存为13字节时,系统内存消耗为24字节
总结
malloc()函数创建的实际空间大小=系统最小分配空间大小+超出部分内存对齐后的值
注意:下例Stm32内存对齐系数以8字节为准
例如:
malloc(1)
在Win10 64下实际创建(1+8)=系统最小分配空间32+超出部分0=32字节
在Stm32下实际创建(1+8)=系统最小分配空间8+(超出部分1->内存对齐后8)=16字节
再例如:
malloc(32)
在Win10 64下实际创建(32+8)=系统最小分配空间32+(超出部分8->内存对齐后16)=48字节
在Stm32下实际创建(32+8)=系统最小分配空间8+(超出部分32->内存对齐后32)=40字节
再例如:
malloc(47)
在Win10 64下实际创建(47+8)=系统最小分配空间32+(超出部分23->内存对齐后32)=64字节
在Stm32下实际创建(47+8)=系统最小分配空间8+(超出部分47->内存对齐后48)=56字节


相信很多带佬不用看我上面的废话,直接看例子就能看懂了。