结构体对齐规则:
(1)每个成员分别按自己的对齐字节数和指定对齐字节数(Linux 64位默认为8)小的那个对齐,这样可以最小化长度。如在32bit的机器上,int的大小为4,因此int存储的位置都是4的整数倍的位置开始存储。
(2)复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,结构体数组的时候,可以最小化长度。
(3)结构体对齐后的长度必须是成员中最大的对齐参数(PPB)的整数倍,这样在处理数组时可以保证每一项都边界对齐。
(4)结构体作为数据成员的对齐规则:在一个struct中包含另一个struct,内部struct应该以它的最大数据成员大小的整数倍开始存储。如 struct A 中包含 struct B, struct B 中包含数据成员 char, int, double,则 struct B 应该以sizeof(double)=8的整数倍为起始地址。
#pragma pack(2) 指定 2 字节对齐(对齐系数2);
#pragma pack(1) 指定 1 字节对齐(对齐系数1);
#include"common.h"
//环境为ubuntu 16.04 64位
//p[]代表内存地址
struct A0{
char a; //p[0]
char b; //p[1]
char c; //p[2]
};
//默认每个变量以(1<4) 1进行对齐,所以长度为 1 + 1 + 1 = 3
struct A1{
char a; //p[0]
int b; //p[4] 4对齐,所以向右偏移3个单位开始存储
long int c; //pp[8] 4对齐,8是4的倍数,不用偏移
char d; //p[16]
};
//每个变量根据自己的类型与指定对齐字节数进行对比,取较小的对齐
//结构体长度为 (1+3) + 4 + 8 + (1+3) + [4] = 24 ------规则3,所以补充4个字节
struct A2{
char a; //p[0]
int b; //p[4] 4对齐,所以向右偏移3个单位开始存储
char c; //[8]
long int d; //pp[16] 8对齐,向右偏移7个单位开始存储
char e; //p[24]
};
//每个变量根据自己的类型与指定对齐字节数进行对比,取较小的对齐
//结构体长度为 (1+3) + 4 + (1+7) + 8 + 1 + [7] = 32
#pragma pack(4)
struct A3{
char a; //p[0]
int b; //p[4]
char c; //[8]
long int d; //pp[12] 4对齐,12就可以,不必要再偏移至16
char e; //p[20]
};
#pragma pack()
//指定对齐系数为4
//结构体长度为 (1+3) + 4 + (1+3) + 8 + 1 + [3] = 24
//--------------------------------------------------//
struct B0{
char a; //p[0]
int b; //p[4]
short c; //p[8]
char d; //p[10]
int f; //p[12]
long int e; //p[16]
};
//对齐系数默认为8
//结构体长度为 (1+3) + 4 + 2 + (1+1) + 4 + 8 = 24
struct B1{
char a; // p[0]
long int b; //p[8]
short c; // p[16]
struct B0 d; //p[24] //最大数据成员为8,所以要以24开始存储
};
//对齐系数默认为8
//结构体长度为 (1+7) + 8 + (2+6) + 24 = 48
#pragma pack(1)
struct B2{
char a; //p[0]
int b; //p[1]
short c; //p[5]
char d; //p[7]
int f; //p[8]
int e; //p[12]
};
#pragma pack()
//对齐系数为1
//结构体长度为 1 + 4 + 2 + 1 + 4 + 4 = 16
struct B3{
char a; //p[0]
int b; //p[4]
short c; //p[8]
struct B2 d; //p[12] //最大数据成员为4,所以要以12开始存储
};
//对齐系数默认为8
//结构体长度为 (1+3) + 4 + (2+2) + 16 = 28
//-------------------------------------------------//
struct C0{
unsigned a : 1; //会紧凑的以4字节分配内存
};
//结构体长度为 4
struct C1{
int a : 1; //会紧凑的以4字节分配内存
};
//结构体长度为 4
struct C2{
char a : 1; //会紧凑的以1字节分配内存
};
//结构体长度为 1
struct C3{
char a : 1; //会紧凑的以1字节分配内存
char b : 2;
char c : 3 ;
};
//1字节够用,所以长度为1
struct C4{
char a : 1;
char b : 2;
int c : 1 ; //会紧凑的以4字节分配内存
};
//长度为4
struct C5{
char a : 1;
char b : 2;
int c : 31;
int d : 20; //会紧凑的以4字节分配内存
};
//长度为 4(a&b) + 4(c) + 4(d) =12
struct C6{
unsigned a : 1;
long b : 32; //会紧凑的以8字节分配内存
int c : 15;
unsigned d : 20;
unsigned e : 30;
unsigned f : 20;
char g;
};
//结构体长度为 8(a&b&c) + 8(d&e) + 8(f&g) = 24
int main()
{
//分析位结构体的内存分配(普通结构体可直接打印地址)
struct C6 my;
memset((char*)&my,0,sizeof(struct C6));//一定要清空,否则间隙的内存可能会有乱的数据影响分析
my.a = 1;
my.b = 2;
my.c = 3;
my.d = 4;
my.e = 5;
my.f = 6;
my.g = 7;
int fp = open("test.bin",O_RDWR | O_CREAT,0666);
write(fp,(char*)(&my),sizeof(struct C6));
close(fp);
//
printf("A0== %ld\n",sizeof(struct A0 ));
printf("A1== %ld\n",sizeof(struct A1 ));
printf("A2== %ld\n",sizeof(struct A2 ));
printf("A3== %ld\n",sizeof(struct A3 ));
printf("B0== %ld\n",sizeof(struct B0 ));
printf("B1== %ld\n",sizeof(struct B1 ));
printf("B2== %ld\n",sizeof(struct B2 ));
printf("B3== %ld\n",sizeof(struct B3 ));
printf("C0== %ld\n",sizeof(struct C0 ));
printf("C1== %ld\n",sizeof(struct C1 ));
printf("C2== %ld\n",sizeof(struct C2 ));
printf("C3== %ld\n",sizeof(struct C3 ));
printf("C4== %ld\n",sizeof(struct C4 ));
printf("C5== %ld\n",sizeof(struct C5 ));
printf("C6== %ld\n",sizeof(struct C6 ));
}
用UE软件打开所存的文件内容为:
经过大小端处理之后:
把开始的8个字节进行分析,先转换为二进制:
可以发现,在C6结构体中,a占了1位,b占了32位,c占了15位,剩余的64-1-32-15间隙
中间8个字节:
d和e每个分了32位,暂时不清楚原因。
末尾8个字节:
f分了24位,其它的分给g以及空隙