数据结构中的数据变量是按定义的先后顺序来排放的,第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放,并且结构的长度必须为成员中自身对齐值最大的那个值的整数倍,不够就补空字节
例子分析:
分析例子B:
struct B
{
char b;
int a;
short c;
};
假设B从地址空间0x0000开始存放,char类型1个字节,int类型4个字节,short类型2个字节,所以对齐值为4。在0x0000存放的是b,之后补上3个空字节,在0x0004到0x0007这四个连续的空间存放的是a,在0x0008到0x0009这两个字节空间中,存放的是c,根据结构体长度整除对齐值的规则,在0x000A到0x000B中补上空字节就满足对齐了。
结构体自身的对齐
1.如果没有明确写上paragma pack(x),结构体自身就按照结构体里最大成员长度对齐
比如
typedef struct _FILE_BASIC_INFORMATION
{
LARGE_INTEGER CreationTime; //8字节
LARGE_INTEGER LastAccessTime; //8字节
LARGE_INTEGER LastWriteTime; //8字节
LARGE_INTEGER ChangeTime; //8字节
USHORT FileAttributes; //4字节
}FILE_BASIC_INFORMATION;
整个结构体按sizeof(LARGE_INTEGER)对齐
比如
pragma pack(4)
typedef struct _FILE_BASIC_INFORMATION
{
LARGE_INTEGER CreationTime; //8字节
LARGE_INTEGER LastAccessTime; //8字节
LARGE_INTEGER LastWriteTime; //8字节
LARGE_INTEGER ChangeTime; //8字节
USHORT FileAttributes; //4字节
}FILE_BASIC_INFORMATION;
比如
pragma pack(8)
typedef struct _x
{
char a;
short b;
}x;
sizeof(x) = 4 (按sizeof(short)对齐)
struct C
{
char b;
int a;
short c;
};
第一个变量b的自身对齐值为1,指定对齐值为2,所以,其有效对齐值为1,假设C从0x0000开始,那么b存放在0x0000,符合 0x0000%1= 0;第二个变量,自身对齐值为4,指定对齐值为2,所以有效对齐值为2,所以顺序存放在0x0002、0x0003、0x0004、0x0005四个连续字节中,符合0x0002%2=0。第三个变量c的自身对齐值为2,所以有效对齐值为2,顺序存放在0x0006、0x0007中,符合 0x0006%2=0。所以从0x0000到0x00007共八字节存放的是C的变量。又C的自身对齐值为4,所以C的有效对齐值为2。又8%2=0,C 只占用0x0000到0x0007的八个字节。所以sizeof(struct C)=8。
sizeof(FILE_BASIC_INFORMATION) = 40
2.如果明确写上paragma pack(x)则又分为两种情况:
a.如果x小于结构体长度最大的成员的长度 则按x对齐
sizeof(FILE_BASIC_INFORMATION) = 36(按4对齐而不是按sizeof(LARGE_INTEGER)
b.如果x大于结构体长度最大的成员的长度 则按结构体里长度最长成员长度对齐
举例:
#pragma pack (2)
总之,编译器是按照什么样的原则进行对齐的?成员对齐有一个重要的条件:即每个成员按自己的方式对齐。其对齐的规则是,每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里默认是8字节)中较小的一个对齐。并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节。