结构体中的成员可以是不同的数据类型,成员按照定义时的顺序依次存储在连续的内存空间。和数组不一样的是,结构体的大小不是所有成员大小简单的相加,需要考虑到系统在存储结构体变量时的地址对齐问题。看下面这样的一个结构体:
struct stu1
{
int i;
char c;
int j;
};
先介绍一个相关的概念——偏移量。偏移量指的是结构体变量中成员的地址和结构体变量地址的差。结构体大小等于最后一个成员的偏移量加上最后一个成员的大小。显然,结构体变量中第一个成员的地址就是结构体变量的首地址。因此,第一个成员i的偏移量为0。第二个成员c的偏移量是第一个成员的偏移量加上第一个成员的大小(0+4),其值为4;第三个成员j的偏移量是第二个成员的偏移量加上第二个成员的大小(4+1),其值为5。
实际上,由于存储变量时地址对齐的要求,编译器在编译程序时会遵循两条原则:一、结构体变量中成员的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍) 二、结构体大小必须是所有成员大小的整数倍。
对照第一条,上面的例子中前两个成员的偏移量都满足要求,但第三个成员的偏移量为5,并不是自身(int)大小的整数倍。编译器在处理时会在第二个成员后面补上3个空字节,使得第三个成员的偏移量变成8。
对照第二条,结构体大小等于最后一个成员的偏移量加上其大小,上面的例子中计算出来的大小为12,满足要求。
再看一个满足第一条,不满足第二条的情况
struct stu2
{
int k;
short t;
};
成员k的偏移量为0;成员t的偏移量为4,都不需要调整。但计算出来的大小为6,显然不是成员k大小的整数倍。因此,编译器会在成员t后面补上2个字节,使得结构体的大小变成8从而满足第二个要求。由此可见,大家在定义结构体类型时需要考虑到字节对齐的情况,不同的顺序会影响到结构体的大小。对比下面两种定义顺序
struct stu3
{
char c1;//偏移量为0符合要求
int i;//偏移量为1, 结构体变量中成员的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍),故偏移量应为4
char c2;//偏移量为8(偏移量4+int大2小4),符合要求
}
算出sizeof( stu3 )=1+8=9,但9不是int的整数倍故最终大小为12
struct stu4
{
char c1; //偏移量为0符合要求
char c2;// //偏移量为1符合要求
int i; //偏移量为2不符合要求故偏移量为4
}
算出sizeof( stu4 )=4+4=8,8是int的整数倍故最终大小为8
如果结构体中的成员又是另外一种结构体类型时应该怎么计算呢?只需把其展开即可。但有一点需要注意,展开后的结构体的第一个成员的偏移量应当是被展开的结构体中最大的成员的整数倍。看下面的例子:
struct stu5
{
short i;
struct
{
char c;
int j;
} ss;
int k;
}
结构体stu5的成员ss.c的偏移量应该是4,而不是 2。整个结构体大小应该是16。
四个重要的基本概念:
1.数据类型自身的对齐值:
对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。
2.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
3.指定对齐值:#pragma pack (value)时的指定对齐值value。
4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。
例子分析:
分析例子B;
struct B
{
char b;
int a;
short c;
};
对于结构体B,
先可以看出这个结构体的每一个成员大小是1 4 2;
其次,再看结构体自身的有效对齐值为4;
每个成员自己的有效对齐值与结构体的有效对齐值比较,得出每个成员的有效对齐值为1 4 2;
有效对齐值决定了偏移量,得出每一个成员的偏移量为0 4 8;
最后得出结构体的大小为最后一个的大小与它的偏移量之和:8+2=10;
检查发现10不是结构体自身有效对齐值(这里是int型)的整数倍,故最后结构体的大小是12.
其实如果就这一个就来说它已将满足字节对齐了, 因为它的起始地址是0,因此肯定是对齐的,之所以在后面补充2个字节,是因为编译器为了实现结构数组的存取效率,试想如果我们定义了一个结构B的数组,那 么第一个结构起始地址是0没有问题,但是第二个结构呢?按照数组的定义,数组中所有元素都是紧挨着的,如果我们不把结构的大小补充为4的整数倍,那么下一 个结构的起始地址将是0x0000A,这显然不能满足结构的地址对齐了,因此我们要把结构补充成有效对齐大小的整数倍.其实诸如:对于char型数据,其 自身对齐值为1,对于short型为2,对于int,float类型,其自身对齐值为4,,double自身对齐值为8,这些已有类型的自身对齐值也是基于数组考虑的,只 是因为这些类型的长度已知了,所以他们的自身对齐值也就已知了.
同理,分析上面例子C:
#pragma pack (2) /*指定按2字节对齐*/
struct C
{
char b;
int a;
short c;
};
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
对于结构体C,
先可以看出这个结构体的每一个成员大小是1 4 2;
其次,再看结构体自身的有效对齐值为2;
每个成员自己的有效对齐值与结构体的有效对齐值比较,得出每个成员的有效对齐值为1 2 2;
有效对齐值决定了偏移量,得出每一个成员的偏移量为0 2 6;
最后得出结构体的大小为最后一个的大小与它的偏移量之和:6+2=8;
检查发现8是结构体有效对齐值2的整数倍,故最后结构体的大小是8.
文章转载来源:http://blog.csdn.net/csw_100/article/details/5495309
(有删改)