#include "stdio.h"
#include "stdlib.h"
#pragma pack (4)
struct A
{
int a;
char b;
short c;
};
#include "stdlib.h"
#pragma pack (4)
struct A
{
int a;
char b;
short c;
};
struct N
{
char b;
int a;
short c;
};
{
char b;
int a;
short c;
};
void main()
{
printf("%d",sizeof(struct A));
printf("%d",sizeof(struct N));
}
结果输出:8 12;
分析如下:首先介绍定义:内存对齐。
现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的
变量的访问可以从任何地址开始,但实际情况并非如此.一些平台对某些特定类型的数据只能从某些特定地址开始存取,这就需要各种
类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放.这就是所谓的字节对齐.字节对齐是为了提高CPU的读取效
率
变量的访问可以从任何地址开始,但实际情况并非如此.一些平台对某些特定类型的数据只能从某些特定地址开始存取,这就需要各种
类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放.这就是所谓的字节对齐.字节对齐是为了提高CPU的读取效
率
那编译器是按照什么样的原则进行对齐的?先看四个重要的基本概念:
1.数据类型自身对齐值:对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节.
2.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值.
3.指定对齐值:#pragma pack (value)时的指定对齐值value.
4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值.
有了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式.有效对齐值N是最终用来决定数据存放地址方式的
值,最重要.有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后
顺序来排放的.第一个数据变量的起始地址就是数据结构的起始地址.结构体的成员变量要对齐排放,结构体本身也要根据自身的有效
对齐值调整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数倍,结合下面例子理解).这样就不难理解上面的几个例
子的值了.
1.数据类型自身对齐值:对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节.
2.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值.
3.指定对齐值:#pragma pack (value)时的指定对齐值value.
4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值.
有了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式.有效对齐值N是最终用来决定数据存放地址方式的
值,最重要.有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后
顺序来排放的.第一个数据变量的起始地址就是数据结构的起始地址.结构体的成员变量要对齐排放,结构体本身也要根据自身的有效
对齐值调整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数倍,结合下面例子理解).这样就不难理解上面的几个例
子的值了.
假设N从地址空间0x0000开始排放。该例子中定义指定对齐值4。第一个成员变量b的自身对齐值
是1,比指定或者默认指定对齐值4小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0。第二个成员变量a,其
自身对齐值为4,所以有效对齐值也为4,所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,符合0x0004%
4=0,且紧靠第一个变量。第三个变量c,自身对齐值为2,所以有效对齐值也是2,可以存放在0x0008到0x0009这两个字节空间中,
符合0x0008%2=0。所以从0x0000到0x0009存放的都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所
以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求,0x0009到0x0000=10字节,(10+2)%4=0。所以0x0000A
到0x000B也为结构体B所占用。故B从0x0000到0x000B共有12个字节,sizeof(struct B)=12;其实如果就这一个就来说它已将满足字
节对齐了,因为它的起始地址是0,因此肯定是对齐的,之所以在后面补充2个字节,是因为编译器为了实现结构数组的存取效率
假设A从地址空间0x0000开始排放。该例子中定义指定对齐值4。第一个成员变量a,其
自身对齐值为4,所以有效对齐值也为4,所以只能存放在起始地址为0x0000到0x0003这四个连续的字节空间中,符合0x0000%
4=0,且紧靠第一个变量。第二个成员变量b的自身对齐值
是1,比指定或者默认指定对齐值4小,所以其有效对齐值为1,所以其存放地址0x0004符合0x0004%1=0。第三个变量c,自身对齐值为2,所以有效对齐值也是2,0x0005%2=1,不满足对齐,可以存放在0x0006到0x0007这两个字节空间中,
符合0x0006%2=0。所以从0x0000到0x0007存放的都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是a)所
以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求,0x0007到0x0000=8字节,8%4=0。所以0x00000
到0x0007也为结构体A所占用。故A从0x0000到0x0007共有8个字节,sizeof(struct A)=8;编译器为了实现结构数组的存取效率.
是1,比指定或者默认指定对齐值4小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0。第二个成员变量a,其
自身对齐值为4,所以有效对齐值也为4,所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,符合0x0004%
4=0,且紧靠第一个变量。第三个变量c,自身对齐值为2,所以有效对齐值也是2,可以存放在0x0008到0x0009这两个字节空间中,
符合0x0008%2=0。所以从0x0000到0x0009存放的都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所
以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求,0x0009到0x0000=10字节,(10+2)%4=0。所以0x0000A
到0x000B也为结构体B所占用。故B从0x0000到0x000B共有12个字节,sizeof(struct B)=12;其实如果就这一个就来说它已将满足字
节对齐了,因为它的起始地址是0,因此肯定是对齐的,之所以在后面补充2个字节,是因为编译器为了实现结构数组的存取效率
假设A从地址空间0x0000开始排放。该例子中定义指定对齐值4。第一个成员变量a,其
自身对齐值为4,所以有效对齐值也为4,所以只能存放在起始地址为0x0000到0x0003这四个连续的字节空间中,符合0x0000%
4=0,且紧靠第一个变量。第二个成员变量b的自身对齐值
是1,比指定或者默认指定对齐值4小,所以其有效对齐值为1,所以其存放地址0x0004符合0x0004%1=0。第三个变量c,自身对齐值为2,所以有效对齐值也是2,0x0005%2=1,不满足对齐,可以存放在0x0006到0x0007这两个字节空间中,
符合0x0006%2=0。所以从0x0000到0x0007存放的都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是a)所
以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求,0x0007到0x0000=8字节,8%4=0。所以0x00000
到0x0007也为结构体A所占用。故A从0x0000到0x0007共有8个字节,sizeof(struct A)=8;编译器为了实现结构数组的存取效率.
分析结束!