C语言结构体内存对齐
在C语言编程中,结构体是一种非常重要的数据类型,它允许我们将不同类型的数据组合在一起。然而,当涉及到结构体在内存中的存储时,有一个关键的概念——内存对齐,这往往容易被忽视,但却对程序的性能和内存使用有着重要影响。
一、结构体大小计算的“理论”与“实际”差异
首先,我们可能会想当然地认为,结构体的大小就是其所有成员大小的简单相加。比如,有这样一个结构体:- struct studentinfo
- {
- char name[128];
- int *p;
- short b;
- int c;
- unsigned int age;
- char sex[20];
- };
复制代码 理论上计算各成员大小之和:128 + 8 + 2 + 4 + 4 + 20 = 166字节(在64位系统中,指针int *p占8字节)。但实际通过sizeof运算符计算时,在64位系统下得到的结果是168字节,和理论值存在偏差。这是为什么呢?
二、内存对齐的原因
这就涉及到内存对齐了。计算机为了提高CPU的寻址效率,在存储数据时会进行内存对齐。一般来说,嵌入式系统多采用32位系统,CPU的地址总线是32位,为了提升CPU的工作效率,寻址通常以4字节为单位。当数据宽度不足4字节时,系统会默认提供4字节内存方便CPU寻址,这种方式就是字节对齐。
我们可以通过示意图来理解:
- 地址未对齐的情形:CPU读取数据时可能需要多次读取,比如要读取一段数据,可能需要读取3次才能获取完整数据。
- 地址已对齐的情形:CPU可以更少次数地读取到完整数据,比如2次就可以,大大提高了效率。
所以,计算结构体大小时考虑内存对齐是典型的“以空间换时间”的案例,用少量的内存空间浪费换取CPU寻址效率的提升。
三、结构体大小计算示例
来看一个具体的题目:- //假设是32bit系统
- #include <stdio.h>
- struct A{
- int i;
- char j;
- char * ptr;
- long Array[100];
- char b[2];
- char * c;
- };
- int main()
- {
- printf("%d\n", sizeof(struct A));
- return 0;
- }
复制代码 在32位系统中,各成员的对齐数(自身大小)如下:
- int i:4字节(对齐数4)
- char j:1字节(对齐数1)
- char *ptr:4字节(指针在32位系统中占4字节,对齐数4)
- long Array[100]:每个long占4字节(对齐数4),数组整体占4×100=400字节
- char b[2]:2字节(对齐数1,数组整体占2字节)
- char *c:4字节(对齐数4)
分步计算过程:
- int i:
从地址0开始存储,占用4字节(地址0~3)。
- char j:
对齐数为1,可紧跟在i之后,从地址4开始,占用1字节(地址4)。
- char *ptr:
对齐数为4,需从4的整数倍地址开始。当前已用地址到4,下一个4的整数倍地址是8,因此从地址8开始存储,占用4字节(地址8~11)。
注意:地址5~7为填充空间(3字节,因j只占1字节,需补齐到4的整数倍才能存放ptr)。
- long Array[100]:
对齐数为4,当前已用地址到11,下一个4的整数倍地址是12,从地址12开始存储,占用400字节(地址12~411)。
- char b[2]:
对齐数为1,紧跟Array之后,从地址412开始,占用2字节(地址412~413)。
- char *c:
对齐数为4,需从4的整数倍地址开始。当前已用地址到413,下一个4的整数倍地址是416,从地址416开始存储,占用4字节(地址416~419)。
注意:地址414~415为填充空间(2字节)。
总大小计算:
所有成员存储结束后,最后一个成员c占用到地址419,此时已用空间为420字节(0~419共420字节)。
由于结构体最大对齐数为4(所有成员的对齐数均不超过4),420是4的整数倍(420÷4=105),满足整体对齐要求。
因此,结构体struct A的大小为420字节。
四、按需分配内存:取消内存对齐
在某些嵌入式产品中,内存大小极其有限,我们希望内核在分配内存单元时采用“按需分配”的原则,也就是取消内存对齐。这时候可以使用C语言标准的预处理指令#pragma pack(n),其中n的值可以是1、2、4、8等,用于进行字节对齐以及取消字节对齐。
例如:- #pragma pack(1) // 取消字节对齐 64bit系统下
- struct studentinfo
- {
- char name[10];
- int *p;
- short b;
- char c;
- int a;
- unsigned int age;
- };
- #pragma pack() // 恢复字节对齐
复制代码 在取消字节对齐后,计算结构体大小就是各成员大小的简单相加,比如上述结构体计算得到的大小是29字节,相比有内存对齐时的大小,节省了内存空间,不过这是以牺牲CPU寻址效率为代价的。
五、总结
内存对齐是C语言中一个重要的概念,它平衡了内存空间和CPU寻址效率。在大多数情况下,默认的内存对齐能够很好地提升程序性能。但在内存资源极度紧张的场景下,我们可以通过#pragma pack指令来取消内存对齐,实现“按需分配”内存。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |
|
|
|
相关推荐
|
|