计算机中内存空间都是按照byte划分的从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。这也是空间和时间的博弈。
#pragma pack()语法
语法:#pragma pack( [show] | [push | pop] [, identifier], n )
作用:指定结构,联合和类的包对齐方式(pack alignment)
结构体的对其规则
结构体中各个成员按照它们被声明的顺序在内存中存储。
- 将数据结构内的所有数据成员相加,计为sum_a;
- 将各数据成员内存对齐,按各自对齐模数而填充的字节数累加到和sum_a上,记为sum_b。对齐模数是【该数据成员所占内存】与【#pragma pack指定的数值】中的较小者。
- 将和sum_b向结构体模数对齐,该模数是【#pragma pack指定的数值】、【未指定#pragma pack时,系统默认的对齐模数8字节】和【结构体内部最大的基本数据类型成员】长度中数值较小者。结构体的长度应该是该模数的整数倍。
基本数据类型所占内存的大小
char | bool | short | int | unsigned int | float | long | double | long double | *p | |
---|---|---|---|---|---|---|---|---|---|---|
32bit编译器 | 1 | 1 | 2 | 4 | 4 | 4 | 4 | 8 | 8 | 4 |
64位编译器 | 1 | 1 | 2 | 4 | 4 | 8 | 8 | 8 | 8 | 8 |
以32位为例:
#pragma pack(4) struct test1{ char c; short sh; int a; float f; int *p; char *s; double d; static double sb; };
静态变量的存放位置与结构体实例的存储位置无关,是单独存放在静态数据取中的,因此用sizeof计算其大小时没有将静态成员所占的空间计算进来。即sizeof(test1)=28 **
这个结构体所占用的总的字节数为28Bytes。C的偏移量为0,占一个Byte。sh占2个Byte,他的对其模数是2(2<4,取小者),存放起始地址应该是2的整数倍,因此c后填充1个空字符,sh的起始地址是2。a占4个Byte,对齐模数是4,因此接在sh后存放即可,偏移量为4。f占4个字节,对齐模数是4,存放地址是4的整数倍,起始地址是8。p,s的起始地址分别是12,16。d占8个字节,对齐模数是4(4<8),d从偏移地址为20处存放。存放后结构体占28个字节,是4的整数倍不用补空字符。
若将test1中的顺序变换
#pragma pack(4) struct test2{ char c; double d; int a; short sh; float f; int *p; char *s; static double sb; };
则该结构体中占用的内存为32Byte,可见写结构体时按各个变量所占内存从小到大排列所占的内存最小。
网友评论