结构体(或者联合体)变量的成员在内存里是如何分布的
1.概述
结构体变量的成员在内存里是如何分布的、成员先后顺序是怎样的、成员之间是连续的还是分散的、还是其他的什么形式?这些问题既和软件相关又和硬件相关。所谓软件相关主要是指和具体的编程语言的编译器的特性相关,编译器为了优化CPU访问内存的效率,在生成结构体成员的起始地址时遵循着某种特定的规则,这就是所谓的 结构体成员“对齐”;所谓硬件相关主要是指CPU的“字节序”问题,也就是大于一个字节类型的数据如int类型、short类型等,在内存中的存放顺序,即单个字节与高低地址的对应关系。
2.分布规则
(1)为了提高CPU访问内存的效率,程序语言的编译器在做变量的存储分配时进行了优化处理,处理的原则是:对于n字节的元素,它的首地址能被n整除,这种原则称为“对齐”。
(2)结构体(联合体)的成员所占内存地址依次增高,第一个成员位于低地址处,最后一个成员位于高地址处,但是结构体成员的内存并不是连续的,编译器会对成员做上述“对齐”处理。
(3)通常编译器可以设置一个对齐参数n,结构体中每个成员实际对齐参数N根据N=min(sizeof(成员类型),n)来得到。结构体成员的内存偏移地址x,满足条件x%N=0。
(4)结构体所有成员的对齐参数N的最大值称为结构体的对齐参数。整个结构的长度必须是结构体对齐参数的最小整数倍,不够补0。
简单来说:
1)结构体的起始地址为x,x%sizeof(结构体)=0。
2)结构体每个成员相对于起始地址偏移地址y,y%N=0。
3)结构体长度等于结构体对齐参数的最小整数倍。
注:编译器对齐参数可以通过指令控制,例如#param pack(2)两字节对齐。vs2010IDE还可以通过"项目属性"->"C/C++"->"代码生成"->“结构成员对齐”来设置。编译器默认对齐参数为8个字节。
例:
struct A { char c; //1byte double d; //8byte short s; //2byte int i; //4byte }; int main(int argc, char*argv[]) { A strua; printf("len:%d\n",sizeof(A)); printf("%d,%d,%d,%d",&strua.c,&strua.d,&strua.s,&strua.i); return 0; }
输出结果为:
len:24
1506156,1506164,1506172,1506176
3.特殊成员
(1)结构体成员为数组时,是将数组的每个元素当一个成员来分配,并不是将整个数组当成一个成员来对待。其他成员的分配规则按照上述规则。
(2)当结构体成员是位段时,存储是按其类型分配空间的,如int型的位段就分配4个字节的存储单元。相邻的同类型的两个位段,如果该类型的长度够用,就将两位段连续存放,共用存储单元,如果不够用,就另起一个该类型长度的存储空间。相邻的不同类型的两个位段,分别为这两个位段分配它们所属类型长度的存储空间。其他成员的分配规则按照上述规则。
1)例1:
struct bit { int a:3; int b:2; int c:3; }; int main(int argc,char* argv[]) { bit s; char *c = (char*)&s; *c = 0x99; cout<<s.a<<endl<<s.b<<endl<<s.c<<endl; return 0; }
输出结果: 1 -1 -4
分析:
hex 0x99
bin 100 11 001
c b a
地址 高 --> 低
cpu为小端模式,高位比特存储在高地址中,低位比特存储在低地址中。而且计算机中存储的是数的补码形式。
2)例2:
struct bit { char a:5; char b:4; char c:7; }; int main(int argc,char* argv[]) { bit s; int *c = (int*)&s; *c = 0x99E00000; cout<<sizeof(bit)<<endl<<(int)s.a<<endl<<(int)s.b<<endl<<(int)s.c<<endl; return 0; }
输出结果:3 0 0 -32
hex 0x99 0xE0 0x00 0x00
bin 10011001 11100000 00000000 00000000
c b a
地址 高 --> 低