为什么要字节对齐?
通常,CPU对数据的访问不可以从任何地址处开始,而是从特定内存地址开始访问的。
假如,CPU每次都从偶数地址处开始访问,这时:
如果一个int型(32位系统,4字节)数据存放在奇地址开始的位置,则CPU需要2个读周期;
如果该int型数据存放在偶地址开始的地方,只需要一个读周期。
显然,为了提高CPU的访问效率,有必研究对数据的存储。这里的对齐指数据不是顺序排放的,二是按照某种规则排放的。
数据类型的自身对齐值
我理解就是数据类型占用字节数(有待确认),如char 自身对齐值为1,short自身对齐值为2,int,float自身对齐值为4,double的自身对齐值?
结构体或类的自身对齐值
成员中自身对齐值最大的那个值。
指定对齐值
#pragram pack (value)
指定对齐值为value.
vc中默认为8,gcc中默认为4.
有效对齐值
自身对齐值和指定对齐值中小的那个值。
结构体的有效对齐值即成员中有效对齐值最大的那个值。
结构体本身要根据自身有效对齐值圆整
就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数倍。
数据存放起始地址
最终由有效对齐值决定数据的存放起始地址,其原则为——存放起始地址%有效对齐值==0,即存放起始地址被有效对齐值整除。
例子,指定对齐值相同,结构体成员的排放顺序不同。
看例子32bit,gcc编译
struct A{
char a;
int b;
short c;
};
sizeof(struct A) == 12;
分析,char int short 的自身对齐值为1 4 2,gcc指定对齐值为4,所以
char int short的有效对齐值即自身对齐值与指定对齐值中较小的那个值为1 4 2.
结构体的有效对齐值为4.
char型成员a有效对齐值为1,因此可存放在任何起始地址除,不妨设a存于0x00000000,
int型成员b有效对齐值为4,所以存完上面的char成员后,只能将int存于0x00000004地址开始处,int型占4字节,因此0x00000004~0x0000007用于存放int.
short型成员c有效对齐值为2,同上分析,存于0x00000008开始处,short两个字节,因此
0x00000008~0x00000009用于存放short.
通过分析知,成员变量存在0x00000000~0x00000009中,共占10个字节,但是还有考虑结构体的圆整性,即结构体成员变量占用总长度需要是对结构体有效对齐值的整数倍,所以A占的内存为0x00000000~0x0000000B 共12个字节(另外两个字节填充)。
struct B{
int a;
char b;
short c;
};
sizeof(struct B) == 8;
同上分析,int char short有效对齐值分别为4 1 2,
根据存放起始地址%有效对齐值==0
假设int存在0x00000000~0x00000003,
char则存在0x00000004,
short则存在0x00000006~0x00000007,
所以int char short 共占有0x00000000~0x00000007八个字节,
再根据结构体圆整性,4的整数倍,所以结构体B共占8个字节。
例子,指定对齐值不同,结构体成员的排放顺序相同。
32位gcc中考虑自身对齐值大于指定对齐值4
struct A{
char a;
double b;
};
sizeof(struct A) == 8;
a b自身对齐值为1 8,指定对齐值为4,所以有效对齐值为1 4,
a存于0x00000000,b存于0x00000004~0x00000007共8个字节,考虑结构体圆整性共8个字节。
来看vc指定对齐值为8的情况
struct A{
char a;
double b;
};
sizeof(struct A) == 16;
a b的自身对齐值分别为1 8,指定对齐值为8,有效对齐值为1 8,
a存放在0x00000000,b存放在0x00000008~0x0000000F共16个字节,
再考虑结构体圆整性最终的16个字节。
掌握 自身对齐值,指定对齐值,有效对齐值,存放起始地址%有效对齐值==0,结构体圆整性。