C语言基础--结构体对齐,位域,联合体
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
C语⾔基础--结构体对齐,位域,联合体
结构体对齐
1--结构体对齐的原因与意义
许多计算机系统对基本数据类型的可允许地址做出了⼀些限制,要求某种类型的对象的地址必须是某个值K(通常是2,4,8)的倍数,⽽这个k则被称为该数据类型的对齐模数(alignment modulus)。
这种对其限制不仅简化了处理器与存储系统之间的接⼝的硬件设计,⽽且提⾼了存储系统的性能。
2 -- 结构体对齐包括两个⽅⾯的含义
1)结构体总长度
2)结构体内各数据成员的内存对齐,即该数据成员相对结构体的起始位置
3--不同编译器的对其策略
1)Linux沿⽤的对其策略是2字节的数据类型(例如short)的地址必须是2的倍数,⽽更⼤的数据类型(如int,double)的地址必须是4的倍数。
2)Windows对齐要求更要严格些,任何K字节(基本)对象的地址都必须是k的倍数。
4 -- 结构体⼤⼩的计算⽅法和步骤
1)将结构体内所有数据成员的长度值相加,记为sum_a;
2)将各数据成员为了内存对齐,按各⾃对齐模数⽽填充的字节数累加到和sum_a上,记为sum_b。
对齐模数是#pragma pack指定的数值以及该数据成员⾃⾝长度中数值较⼩者。
该数据相对起始位置应该是对齐模式的整数倍;
3)将和sum_b向结构体模数对齐,该模数是#pragma pac指定的数值和结构体内部最⼤的基本数据类型成员长度中数值较⼩者。
结构体的长度应该是该模数的整数倍.
4)Linux与Windows基本数据类型⼤⼩以及对齐模数
char short int long double long double
平台长度与对齐
模数
Windows长度124488
对齐模数124488
Linux长度1244812
对齐模数124444
3.例⼦
1)
1: struct my_struct 2: { 3: char a; 4: long double b; 5: };
Windows分析:
步骤1:得出sum_a=1+8=9;
步骤2,数据成员a放在相对偏移0处,之前不需要填充字节;数据成员b为了内存对齐,根据“结构体⼤⼩的计算⽅法和步骤”中第⼆条原则,其对齐模数是8,之前需填充7个字节,sum_a + 7 = 16B --> sum_b = 16 B
步骤3:按照定义,结构体对齐模数是结构体内部最⼤数据成员长度和pragma pack中较⼩者,前者为8后者为4,所以结构体对齐模数是4。
sum_b是4的4倍,不需再次对齐。
综上3步,可知结构体的长度是16B,
Linux分析:
步骤1:同Windows步骤⼀
步骤⼆:由于Linux的long double 对齐模数为4,数据成员b为了内存对齐,之前需填充3个字节,sum_a + 3 = 12B --> sum_b = 12 B
步骤3:按照定义,结构体对齐模数是结构体内部最⼤数据成员长度和pragma pack中较⼩者,前者为8后者为4,所以结构体对齐模数是4。
sum_b是4的4倍,不需再次对齐。
综上3步,可知结构体的长度是12B,
2)
1: #pragma pack(2) 2: struct my_struct 3: { 4: char a; 5: long double b; 6: }; 7: #pragma pack()
Windows分析:
步骤1:得出sum_a=1+8=9;
步骤2,数据成员a放在相对偏移0处,之前不需要填充字节;数据成员b为了内存对齐,根据“结构体⼤⼩的计算⽅法和步骤”中第⼆条原则,其对齐模数是2,之前需填充1个字节,sum_a + 1 = 10B --> sum_b = 10 B
步骤3:按照定义,结构体对齐模数是结构体内部最⼤数据成员长度和pragma pack中较⼩者,前者为8后者为2,所以结构体对齐模数是2。
sum_b是2的5倍,不需再次对齐。
综上3步,可知结构体的长度是10B,
Linux分析:
步骤1:所有数据成员⾃⾝长度和:1B + 12B = 13B --> sum_a = 13B
步骤⼆:数据成员a放在相对偏移0处,之前不需要填充字节;数据成员b为了内存对齐,根据“结构体⼤⼩的计算⽅法和步骤”中第⼆条原则,其对齐模数是2,之前需填充1个字节,sum_a + 1 = 14B --> sum_b = 14 B
步骤3:按照定义,结构体对齐模数是结构体内部最⼤数据成员长度和pragma pack中较⼩者,前者为8后者为2,所以结构体对齐模数是2。
sum_b是2的7倍,不需再次对齐。
综上3步,可知结构体的长度是14B。
位域
有些信息在存储时,并不需要占⽤⼀个完整的字节,⽽只需占⼏个或⼀个⼆进制位。
为了节省存储空间,并使处理简便,C语⾔⼜提供了⼀种数据结构,称为“位域”或“位段”。
所谓“位域”是把⼀个字节中的⼆进位划分为⼏个不同的区域,并说明每个区域的位数。
每个域有⼀个域名,允许在程序中按域名进⾏操作。
这样就可以把⼏个不同的对象⽤⼀个字节的⼆进制位域来表⽰。
⼀、位域的定义和位域变量的说明位域定义与结构定义相仿
其形式为:
1: struct s 2: { 3: Type Name:Size 4: }
1. ⼀个位域必须存储在同⼀个字节中,不能跨两个字节。
2. 由于位域不允许跨两个字节,因此位域的长度不能⼤于⼀个字节的长度。
3. 位域可以⽆位域名,这时它只⽤来作填充或调整位置。
⽆名的位域是不能使⽤的。
⼆、位域的对齐
如果结构体中含有位域(bit-field),那么VC中准则是:
1) 如果相邻位域字段的类型相同,且其位宽之和⼩于类型的sizeof⼤⼩,则后⾯的字段将紧邻前⼀个字段存储,直到不能容纳为⽌;
2) 如果相邻位域字段的类型相同,但其位宽之和⼤于类型的sizeof⼤⼩,则后⾯的字段将从新的存储单元开始,其偏移量为其类型⼤⼩的整数倍;
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩⽅式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩⽅式;
系统会先为结构体成员按照对齐⽅式分配空间和填塞(padding),然后对变量进⾏位域操作。
三.联合体
当多个数据需要共享内存或者多个数据每次只取其⼀时,可以利⽤联合体(union)。
在C Programming Language ⼀书中对于联合体是这么描述的:
1)联合体是⼀个结构;
2)它的所有成员相对于基地址的偏移量都为0;
3)此结构空间要⼤到⾜够容纳最"宽"的成员;
4)其对齐⽅式要适合其中所有的成员;
下⾯解释这四条描述:
由于联合体中的所有成员是共享⼀段内存的,因此每个成员的存放⾸地址相对于于联合体变量的基地址的偏移量为0,即所有成员的⾸地址都是⼀样的。
为了使得所有成员能够共享⼀段内存,因此该空间必须⾜够容纳这些成员中最宽的成员。
对于这句“对齐⽅式要适合其中所有的成员”是指其必须符合所有成员的⾃⾝对齐⽅式。
下⾯举例说明:
如联合体
union U
{
char s[9];
int n;
double d;
};
s占9字节,n占4字节,d占8字节,因此其⾄少需9字节的空间。
然⽽其实际⼤⼩并不是9,⽤运算符sizeof测试其⼤⼩为16.这是因为这⾥存在字节对齐的问题,9既不能被4整除,也不能被8整除。
因此补充字节到16,这样就符合所有成员的⾃⾝对齐了。
从这⾥可以看出联合体所占的空间不仅取决于最宽成员,还跟所有成员有关系,即其⼤⼩必须满⾜两个条件:1)⼤⼩⾜够容纳最宽的成员;2)⼤⼩能被其包含的所有基本数据类型的⼤⼩所整除。
联合体使⽤举例:
测试⼤⼩端
1: static union { char c[4]; unsigned long mylong; } endian_test = {{ 'l', '?', '?', 'b' } };
2: #define ENDIANNESS ((char)endian_test.mylong)。