本篇文章将会对位段、枚举和联合的相关知识进行讲解
位段的声明和结构体类似,但是有两点不同:
1.位段的成员必须是int,unsigned int,signed int (C99之后也可以是其他成员,但一般是int, char)
2.位段的成员的后面都有一个冒号和一个数字,代表该成员所占的比特位
假如A是一个普通的结构体,那么此时A的大小为16个字节,那么位段A的大小是多少?
struct A
{
int a : 2;
int b : 5;
int c : 10;
int d : 30;
};
printf("%zd\n", sizeof(struct A));
得到的结果为8,可以看出位段相比于结构体更能节省空间,那位段是怎么分配内存空间的,为什么A的大小是8个字节?
位段在空间上按照4个字节(int)或一个字节(char)为单位来开辟的,也就是当位段成员主要是int时,它会先开辟4个字节,当不够用时会再开辟4个字节;当位段成员主要是char时,它会先开辟1个字节,当不够用时会再开辟1个字节
除此之外,位段还有许多跨平台问题:
1. int位段究竟是无符号unsigned int还是有符号signed int是不确定的
2. int位段的最大位段数是不确定的,因为在16位机器上int占2个字节,在32位机器上int占4个字节
3. 位段成员在内存中是从左到右分配还是从右到左分配是不确定的,比如还是以上题为例,由于位段成员主要是int类型,所以位段在空间上按4个字节为单位来开辟的,先开辟4个字节:
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的
比如char位段,先开辟一个字节,a占6个比特位,b占4个比特位
下面以一个例题为例:
struct S
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
int main()
{
struct S s = { 0 };
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
return 0;
}
枚举就是一一列举
enum Color
{
RED,
GREEN,
BLUE
};
每一个枚举常量都有对应的值,第一个枚举常量的默认值为0,之后依次递增1,在上述代码中枚举常量RED的值为0,GREEN为1,BLUE为2
enum Color
{
RED,
GREEN,
BLUE
};
int main()
{
printf("%d", RED); //打印结果为0
return 0;
}
我们也可以修改默认值
enum Color
{
RED = 3,
GREEN,
BLUE
};
这样RED的值就变成了3,GREEN和BLUE也依次变为4和5
enum Color
{
RED,
GREEN = 5,
BLUE
};
int main()
{
enum Color clr = GREEN;//给枚举类型的变量赋值时要用枚举常量
clr = 6; //这种形式的代码在vs的.c文件中不会报错,这是因为vs的.c文件对类型检查不够严格,当把.c文件改为.cpp文件时,程序就会报错,所以不建议这样写代码
return 0;
}
联合体(共用体)也是一种自定义类型,它也包含一系列的成员,它的特征是这些成员共用同一空间
union un
{
char c;
int i;
};
int main()
{
union un u; //创建联合体变量
printf("%p\n", &u); //求变量的地址
printf("%p\n", &u.c);
printf("%p\n", &u.i); //求成员的地址
return 0;
}
有打印结果可以看出联合体的内部成员共用同一块空间;
在深度剖析数据在内存中的存储中讲解过大小端存储模式,当时写过判断大小端存储模式的代码:
int check_sys()
{
int a = 1;
return *(char*)&a; //判断该数据的第一个字节是多少
}
int main()
{
int ret = check_sys();
if (ret == 1)
{
printf("小端存储\n");
}
else
{
printf("大端存储\n");
}
return 0;
}
同样我们也可以用联合实现判断大小端的功能
int check_sys()
{
union un
{
char c;
int i;
}u;
u.i = 1;
return u.c;
}
联合体的成员共用同一块内存空间,一个联合体变量的大小至少是其最大成员的大小,因为当最大成员的大小不是最大对齐数的整数倍时就要对齐到最大对齐数的整数倍处
union un
{
char c[5];
int i;
}u;
int main()
{
printf("%zd", sizeof(u)); //结果为8
return 0;
}
上述代码中c的对齐数是1,i的对齐数是4,所以最大对齐数是4,而char c[5]的大小是5个字节,所以要再浪费3个字节对齐到4的倍数也就是8个字节
至此,有关结构体、位段、枚举和联合的知识全部讲解完毕;之后会讲解动态内存管理