结构内存对齐
整形,浮点型和字符型乃至数组都能计算大小,那么结构体的大小怎么计算呢?我们知道结构体成员都是由整形、浮点型字符型等组成的。那结构体大小是不是就是结构体成员大小相加,显然没有那么简单。那么结构体大小到底是怎么计算大小的呢?
计算需要遵循结构体的对齐规则:
- 第一个成员在与结构体变量偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的值为8 - 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
注意:默认对齐数不是所有编译器都有,我这里是VS默认的对齐数是8,而linux下没有默认对齐数,当没有默认对齐数时,成员变量的大小就是该成员的对齐数。
结构体大小的计算
先拿结构体成员的大小和编译器默认对齐数相比较,取较小的那一个为该成员变量的对齐数,我是VS默认对齐数是8
然后再把他们存进内存,存进去的时候就会发生内存对齐。存进去的时候每个成员变量都是存到自己的对齐数的整数倍的位置。
最后通过最大对齐数计算出结构体大小
从图片可以看到红色部分到蓝色部分总大小为12个字节,中间的白色则是被浪费掉的内存。这个结构体的大小为12,刚好是最大对齐数4的整数倍数。所以该结构体在VS的编译器下大小就是12个字节,所以创建这个结构体要在内存上开辟12个字节的内存空间。
注意:大多数情况下,成员变量已经占用的总字节个数并不一定是正好为其成员变量中的最大对齐数的整数倍数,这个时候则会发生内存对齐将内存扩大为最大对齐数的整数倍数。
为什么存在内存对齐?
- 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址
处取某些特定类型的数据,否则抛出硬件异常。 - 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器
需要作两次内存访问;而对齐的内存访问仅需要一次访问
总体来说:结构体的内存对齐是拿空间来换取时间的做法
设计结构体时的小技巧
我们在来看一个列子
对比上一个列子,相同的结构体成员这个占的内存为8而上个列子则为12,那么要怎么样才能避免浪费掉过多的内存空间呢?
struct S1//占12个字节
{
char c1;
int i;
char c2;
};
struct S2//占8个字节
{
char c1;
char c2;
int i;
};
其实很简单,我们只需要把占用内存小的成员尽量集中在一起,就可以有效的避免内存的浪费。
修改默认对齐数
要修改默认对齐数,则需要用到预处理命令
#pragma pack()
这个命令用法很简单,只需要在括号里填上数字就可以修改默认对齐数。同样的如果括号里面不填数字就会恢复为编译器默认的对齐数。
看一下代码
#include <stdio.h>
#pragma pack(1)//修改默认对齐数为1
struct s1
{
char i;
int c;
char b;
};
#pragma pack()//恢复默认对齐数
struct s2
{
char i;
int c;
char b;
};
int main()
{
printf("%d\n", sizeof(struct s1));
printf("%d\n", sizeof(struct s2));
return 0;
}
在结构体对齐方式不合适的时候我们可以修改默认对齐方式。