1.位段
什么是位段
位段的声明和结构体是类似的,但有两个不同的地方。
- 位段的成员必须是int,unsinged int 和signed int 或者是char(属于整型家族)类型
- 位段的成员名后有一个冒号和一个数字
例如:
struct A
{
int a : 2;
int b : 5;
int c : 10;
int d : 30;
};
那位段A的大小是多少?
printf("%d\n",sizeof(struct A));
为什么是8呢? 在位段中:
位:二进制位
struct A
{
int a : 2;
int b : 5;
int c : 10;
int d : 30;
};
- 4个成员变量都是int类型的,最开始只开辟4个字节的空间,然后在它们分别占用的比特位空间处放入数据
- 最开始开辟4个字节,共32个bite位,a占2个,b占5个,c占10个,此时一共使用了17个bite位,还剩15个,d占30个,剩余不够d使用,在重新开辟4个字节将d放入其中,共使用8个字节。
- 放入方式是固定的一种形式,下面便是我们对这种方式的学习。
位段的内存分配
- 位段的成员可以是int、unsigned int、signed int 或 char类型
- 位段的空间上是按照需要以4个字节(int)或1个字节(char)的方式来开辟的
- 位段涉及很多不确定因素,位段是不跨平台的,注意可以执行的程序应该避免使用位段。
- 位段中int类型和char类型一般是不会混着用的,位段有很多的不确定性,所以我们在使用的时候一定要谨慎。
例子:
struct B
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
int main()
{
struct B b = { 0 };
b.a = 10;
b.b = 12;
b.c = 3;
b.d = 4;
? return 0;
}
- 位段占据内存的规则
- 位段数据在内存中的存储
- VS调试展示内存
这些可以说明,在VS下 - 位段开辟内存是按照成员变量的类型开辟空间的,当开辟的空间不够时,才会去增加1个对应类型大小的空间
- 位段在内存中存储的方式是小端存储,由低地址向高地址存储数据
- 分配方式是从右向左,从一个bite位的最低位开始存入数据。
- C语言并没有规定这些规则,这些规则适用于VS编译器,不同的平台产生的效果是不同的,所以它是不支持跨平台的。
位段的跨平台问题
- int位段被看作有符号数还是无符号数是不确定的。
- 位段中最大位的数目不确定。(16位机器最大16,32位机器最大32)当我们把一个成员变量所占bite位写成27,在16位机器会出现问题。
数据的顺序不变,接收的顺序不同。 - 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义.
如果数据传输到不同平台的顺序相同,而分配和读取的方向不同,这明显是不对的 - 当一个结构包含两个位段,第二个位段成员比较大,无法容纳第一个位段剩余部分,是舍弃剩余的位还是利用,这是不确定的。
总结:
位段可以达到和结构体相同的作用,而且可以更好的节省空间,但是有跨平台性的问题存在。
位段的应用
- 使用位段式的结构合理的利用空间,将传输的数据分块给出固定的大小,减少数据的传输,提高数据传输的效率。
2.枚举
枚举顾名思义就是 一 一列举 把可能的取值 一 一 列举。 比如我们现实生活中:
一周的星期一到星期日是有限的7天,可以一 一例举。 性别有:男、女、保密,也可以一 一列举。 月份有12个月,也可以一 一例句。 这里就可以使用枚举了。
枚举类型的定义
enum Color
{
RED,
GREEN,
BLUE
};
enum Day
{
MON,
TUES,
WED,
THUR,
FRI,
SAT,
SUN
};
enum Sex
{
MALE,
FEMALE,
SECRET
};
以上定义的enume Color,enum Day,enum Sex都是枚举类型。
- 结构体大括号内的变量叫:结构体的成员变量
- 枚举的大括号内是:枚举的可能取值也叫“枚举常量”
因为它是常量,在定义枚举的大括号外,无法给它赋值。
这些可能取值都是有值的,默认从0开始,一次递增1
enum color
{
RED,
GREEN,
BLUE
};
int main()
{
printf("%d\n", RED);
printf("%d\n", GREEN);
printf("%d\n", BLUE);
return 0;
}
在定义的时候也可以赋初值
enum color
{
RED=1,
GREEN=4,
BLUE
};
int main()
{
printf("%d\n", RED);
printf("%d\n", GREEN);
printf("%d\n", BLUE);
return 0;
}
一个枚举类型的大小只有四个字节,每次取出的值为枚举的可能取值,它们不是同时在枚举类型中存储,一次只调用一种可能。(不同编译器下,枚举的大小可能会取不同的值)
enum color
{
RED,
GREEN,
BLUE
};
int main()
{
printf("%d\n", sizeof(enum color));
return 0;
}
枚举的优点
我们可以使用#define定义常量,为什么非要使用枚举?
枚举的优点:
- 增加代码的可读性和可维护性
当使用switch时可以将case后的选项替换为枚举的可能选择
- 和#define定义的标识符比较枚举有类型检查,更为严谨。
#define定义的常量是直接替换掉,而枚举常量是有自己的类型的 1. 2.
- 防止了命名污染
将枚举常量放在大括号中,对其进行封装防备受到污染。
- 便于调试
枚举在调试中是可以直接看到的,而#define是直接替换
- 使用方便,一次可以定义多个常量。
3.联合(共用体)
联合类型的定义
联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员,特征是这些成员共用同一块空间(所以联合也叫共用体)。
例如:
union Un
{
char c;
int i;
};
union Un un;
联合的特点
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大得那个成员)
union Un
{
char c[5];
int i;
};
int main()
{
union Un un;
printf("%d\n", sizeof(un));
printf("%d\n", sizeof(un.c));
printf("%d\n", sizeof(un.i));
printf("%d\n", &(un.c));
printf("%d\n", &(un.i));
return 0;
}
- 这里我们可以看到,不同类型的联合体成员的大小是不同的,但它们的地址却是相同的。这就是特征的表现——共用同一块内存空间。
union Un
{
char c;
int i;
};
int main()
{
union Un un;
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n", un.i);
printf("%x\n", un.c);
return 0;
}
- 当联合体一个成员的值发生改变会影响到其他成员在该空间的改变
- 在联合体中的变量调用时最多只能有一个成员变量被调用,否则会使存储的数据变的很混乱。
联合大小得计算
- 联合的大小至少是最大成员的大小
最少要保证可以存储最大的成员的数据。 - 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
union Un
{
char c[7];
int i;
};
int main()
{
union Un un;
printf("%d\n", sizeof(un));
return 0;
}
使用联合体判断大小端
在之前博客数据得存储中,讲到了判断大小端得方法,使用int和char的交换判断 这里我们使用新的方法重新判断
union Un
{
int a;
char b;
};
int main()
{
union Un un;
un.a = 0x1;
int flag = un.b;
if (flag == 1)
printf("小端\n");
else
printf("大端\n");
return 0;
}
|