前言
在初步学习了结构体之后我们需要更深层次的了解和学习结构体具有的一些性质
此篇文章讲述结构体的对齐规则和位段
一、结构体的对齐规则
了解结构体的对齐规则的重要作用就是计算结构体的大小
根据规定有以下规则:
1.结构体的第一个成员永远都放在0偏移处
2.从第二个成员开始,以后的每个成员都要对齐到某个对齐数的整数倍处
这个对齐数是成员自身大小和默认对齐数的较小值
注意:VS环境下默认对齐数是8,gcc环境下没有默认对齐数,没有默认对齐数时,对齐数就是成员自身的大小
3.当成员全部存放进去后,结构体的总大小必须是所有成员的对齐数中最大对齐数的整数倍。如果不够,则浪费空间对齐。
4.如果嵌套了结构体,嵌套的结构体成员要对齐到自己成员的最大对齐数的整数倍处。
整个结构体的大小,必须是最大对齐数的整数倍,最大对齐数包含中嵌套的结构体成员中的对齐数。
在简略了解规则之后,我们举几个例子帮助深刻理解对齐规则
例1.
struct S1
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(struct S1));
我们放到编译器里面验证
例2.
struct S2
{
char c1;
char c2;
int i;
};
printf("%d\n", sizeof(struct S2));
例3.
struct S3
{
double d;
char c;
int i;
};
printf("%d\n", sizeof(struct S3));
例4.
struct S4
{
char c1;
struct S3 s3;
double d;
};
printf("%d\n", sizeof(struct S4));
二、结构体位段
结构体位段的使用能够有效减少内存空间的使用
位段的声明和结构是类似的,有两个不同:
1、位段的成员必须是 int 、unsigned int 、或signed int以及char类型(只要是整型家族就行,一般都是相同的类型出现)
2、位段的成员名后边有一个冒号和一个数字(数字的含义是该变量给定的bit位大小)
例如:
struct A
{
int a:2;
int b:5;
int c:10;
int d:30;
};
在vs的编译器情况下 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时会舍弃剩余的位
所以上述代码 在该结构体向int对齐的情况下开辟一个段位会给定32个bit位四个字节,a,b,c三个变量一共占17个bit位 只剩余了15个bit位而d变量需要占30个bit位 所以d变量会重新开辟一个段位 也就是上述变量一共需要64个bit位或者说8个字节的空间
其次在使用位段时,我们需要注意
- 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
- 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的
- 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段
虽然位段的使用能够减少空间的使用,但局限性很大,不仅仅在使用情况上存在限制,而且存在平台的不兼容性
位段的跨平台问题
- int 位段被当成有符号数还是无符号数是不确定的
- 位段中最大位的数目不能确定,16位机器最大16,32位机器最大32,写成27,在16位机器会出问题
- 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义
- 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的
所以位段的使用需要特定的条件,并且还需要考虑跨平台的不兼容性