目录
2. 位段
2.1 什么是位段
位段的声明和结构是类似的,有两个不同:
1.位段的成员必须是 int、unsigned int 或signed int 。
2.位段的成员名后边有一个冒号和一个数字。
//位段
#include<limits.h>
struct AA
{
int _a;//INT_MIN ~ INT_MAX
int _b;
int _c;
int _d;
};
struct A
{
int _a : 2;//_a这个成员只占两个bit位就可以了
int _b : 5;//_b这个成员只占五个bit位
int _c : 10;
int _d : 30;
};
int main()
{
printf("%d\n", sizeof(struct AA));//16个字节
printf("%d\n", sizeof(struct A));//8个字节
return 0;
}
根据场景,现实及需求,选择是否使用位段
2.2 位段的内存分配
- 1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
- 2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
- 3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
//位段在内存中的使用
struct A
{
int _a : 2;//首先分配4个字节,_a用了2个bit位 30
int _b : 5;//_b再次使用了5个bit位 25
int _c : 10;//_c再次使用了10个bit位 15
int _d : 30;//_d不够用,需要再次开辟4个字节
};
int main()
{
printf("%d\n", sizeof(struct A));//8个字节
return 0;
}
//一个例子
struct S
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
int main()
{
printf("%d\n", sizeof(struct S));//3个字节
struct S s = { 0 };
s.a = 10;//1010,放进去3bit:010
s.b = 12;//1100,放进去4bit:1100
s.c = 3;//11,放进去5bit:00011
s.d = 4;//100,放进去4个bit:0100
//0110 0010 0000 0011 0000 0100
// 先使用低位地址分配,不够继续向高位开辟
// 0x 6 2 0 3 0 4
//小端存储模式:040362
return 0;
}
2.3 位段的跨平台问题
- 1. int 位段被当成有符号数还是无符号数是不确定的。
- 2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机 器会出问题。
- 3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
- 4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是 舍弃剩余的位还是利用,这是不确定的。
早期16位机器:sizeof(int) _2个字节
32/64位机器: sizeof(int)_4个字节
总结:
跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。
3. 枚举
枚举顾名思义就是一一列举。
把可能的取值一一列举。
比如我们现实生活中:
一周的星期一到星期日是有限的7天,可以一一列举。
性别有:男、女、保密,也可以一一列举。
月份有12个月,也可以一一列举
3.1 枚举类型的定义
enum Day//枚举类型
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,//枚举常量
Sun
};
enum Sex//性别
{
MALE,
FEMALE,
SECRET
};
enum Color//颜色
{
RED,
GREEN,
BLUE
};
int main()
{
enum Sex s = MALE;
enum Sex s2 = FEMALE;
return 0;
}
以上定义的 enum Day , enum Sex , enum Color 都是枚举类型。
{}中的内容是枚举类型的可能取值,也叫 枚举常量 。
这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值。
例如:
enum Sex//性别
{
MALE,
FEMALE,
SECRET
};
//enum Color//颜色
//{
// RED,
// GREEN,
// BLUE
//};
enum Color//颜色
{
RED = 1,
GREEN = 2,//这里叫做常量初始化,并不是修改常量
BLUE = 4
};
int main()
{
/*enum Sex s = MALE;
enum Sex s2 = FEMALE;
enum Day d = Fri;*/
printf("%d %d\n", MALE, RED);//0 1
return 0;
}
这里叫做常量初始化,并不是修改常量
3.2 枚举的优点
为什么使用枚举?
我们可以使用 #define 定义常量,为什么非要使用枚举?
枚举的优点:
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 防止了命名污染(封装)
4. 便于调试
5. 使用方便,一次可以定义多个常量
3.3 枚举的使用
enum Color//颜色
{
RED = 1,
GREEN = 2,
BLUE = 4
};
int main()
{
enum Color clr = GREEN;//这里会进行类型检查
//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。
clr = 5;
printf("%d\n", clr);//5
return 0;
}
4. 联合(共用体)
4.1 联合类型的定义
联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。
//联合体——共用体
union Un
{
char c;
int i;
};
int main()
{
union Un u;
printf("%u\n", sizeof(u));//4
printf("%p\n", &u);//0073FAD8
printf("%p\n", &(u.c));//0073FAD8
printf("%p\n", &(u.i));//0073FAD8
return 0;
}
4.2 联合的特点
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。
union Un
{
char c;
int i;
};
int main()
{
union Un u;
printf("%u\n", sizeof(u));//4
printf("%p\n", &u);//0073FAD8
printf("%p\n", &(u.c));//0073FAD8
printf("%p\n", &(u.i));//0073FAD8
u.i = 0x11223344;
u.c = 0x55;
printf("%x\n", u.i);//11223355
//联合体使用只使用一次,因为改变了c就会改变i
//改变了i就会改变c
return 0;
}
4.2.1 联合体的应用:判断大小端字节序存储模式
//判断大小端
int check_sys()
{
union Un
{
char c;
int i;
}u;
u.i = 1;
//01 00 00 00 小端
return u.c;
//01
}
int main()
{
//int i = 1;
01 00 00 00 小段存储
//if (*(char*)&i)
// printf("小端字节序存储模式\n");
//else
// printf("大端字节序存储模式\n");
if (1 == check_sys())
printf("小端\n");
else
printf("大端\n");
}
4.3 联合大小的计算
联合的大小至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
联合体也是存在对齐的
typedef union
{
char arr[5];//对齐数1
int i;//对齐数是4
//最大对齐数位4,但是arr占5个字节
}Un;
int main()
{
Un u;
printf("%u\n", sizeof(u));//8
return 0;
}
typedef union
{
short arr[7];//14
int i;//4
//最大对齐数是 4 ,但是short占了14个字节
//因此联合体的大小应该是最大对齐数的整数倍,16
}Un;
int main()
{
Un u;
printf("%u\n", sizeof(u));//16
return 0;
}