目录
一、结构体的内存对齐
1.内存对齐规则
1.第一个成员在结构体变量偏移量为0的地址处;
2.其他变量要对齐对齐数的整数倍的地址(对齐数=编译器默认的对齐数与该成员大小的较小值,其中vs中默认对齐数为8,Linux中没有默认对齐数,对齐数大小就是成员自身的大小);
3.结构体总大小为最大对齐数的整数倍;
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己最大对齐数的整数倍,结构体的大小是所有最大对齐数(包含嵌套结构体的对齐数)的整数倍。
2.举例说明
由不同变量的偏移量我们可以得出变量开辟空间情况如下:
根据第一条规则c1开辟在漂移量为0处,c2的类型为char,大小为1个字节小于8个字节,对齐数取1,对齐在其整数倍的地址处,所以对齐在偏移量1处,i同理对齐数取4,因其对其在整数倍对齐数地址处,故2,3地址处浪费掉,对其在偏移量为4地址处,最大对齐数为4,结构体大小为4的整数倍故该结构体大小为8字节(该类解释后文不再赘述)。
结构体大小为8字节c2后有2个字节浪费掉。
结构体大小为12字节,中间有3个,最后有3个字节浪费掉。
结构体大小为12字节,中间有3个,最后有3个字节浪费掉。
不难看出,为了节省空间我们要让占用空间小的成员聚集在一起。
补充一下,默认对齐数是可以设置的,方式如下:
在创建的结构体类型前加上#pragma pack(),括号内是要修改的默认对齐数。
3.为什么存在内存对齐?
1.平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的,某些硬件平台只能在某些特定地址读取某些特定类型的数据,否则抛出硬件异常;
2.性能原因:
数据结构(尤其是栈)应该尽可能的在自然边界上对齐。原因在于,为了访问未对齐的内存,操作系统需要做两次访问,而对齐的内存仅需一次访问。
举个例子(32位平台下)访问i:
若未对齐:
第一次访问:
第二次访问:
若对齐:
仅需一次就可访问完i;
总结:结构体内存是拿空间换取时间的做法,而在设计时既要满足对齐又要满足节省空间,我们要将占用空间小的成员尽可能聚集在一起。
二、联合体
1.联合体的简单介绍
联合体也是一种自定义类型,这种类型定义的变量也包括一系列成员,类似于结构体联合体定义变量的成员也可以是不同类型的,特点是共用一块空间。
2.举例说明
由运行结果可得创建的联合体变量,联合体变量成员地址一致,验证前文所提成员共用同一块空间。
3.联合体大小的计算
1.联合体大小至少是最大成员的大小;
2.当联合体大小不是最大对齐数的整数倍时要对齐到最大对齐数的整数倍。
该char类型的数组c可以看做6个char类型的变量,每个对齐数为1,i的对齐数为4,因要对齐到最大对齐数的整数倍即4的整数倍故该联合体的大小为8,即i与c共用4字节后再用2字节,因对齐到8再次申请2字节空间。
4.联合体的简单应用
验证当前机器是大端存储还是小端存储?
法一(利用指针):
int check_sys()
{
int i = 1;
return *(char*)&i;
}
int main()
{
if (check_sys())
printf("小端存储\n");
else
printf("大端存储\n");
return 0;
}
利用不同类型指针访问步调来判断;
法二(利用联合体):
int main()
{
union Un
{
char c;
int i;
};
union Un u;
u.i = 1;
printf("%d\n", u.c);
return 0;
}
利用联合体共用空间来通过访问c对i进行部分数据访问。