二.位段
2.1 位段的介绍
位段的声明和结构体是大同小异的,仅有两个地方不一样:
1、位段的成员必须是int ,unsigned int 或signed int ,char等整型家族。
2、位段的成员名后面有一个冒号和一个数字。
比如:
我们可以看到,A的大小是8个字节,64bit,而不是47bit,近似6个字节,所以说明位段有它独特的内存开辟方式。
注意:冒号后面的数字表示几个bit,而不是字节。
2.2 位段的内存分配
2、位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式开辟的
3、位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段
通过上述,我们发现位段是一次一个字节,或者4个字节开辟的。
我们再看以下代码:
在这里我们首先要注意冒号后面的数字(bit)不能大于它的类型。其次我们看到S的大小是3,如果把S里面的所有bit加起来恰好是16bit,也就是两个字节,但是出现了3,说明:
说明中间浪费了一个bit位,因为按照不浪费的形式是两个bit位就够了,但是放完_b之后只剩一个bit不够_c的5个bit,所以又开辟,但是那一个bit浪费了。
2.3 位段的截断
位段的类型是可以赋值的,但是由于我们设置了bit限制,所以这里对于如何处置这些值是不清楚的。我们可以分析以下代码:
struct S { char _a : 3; char _b : 4; char _c : 5; char _d : 4; //冒号后面的数字不能超过类型的大小 }s; int main() { s._a = 10; s._b = 12; s._c = 3; s._d = 4; return 0; }
很明显,这里的10转化为二进制是1010,需要4个bit位,而位段限制只有3个bit可以给,所以我们要深入到内存的存储来看一下vs是如何安排的。
我们可以调试验证一下是不是不这样。
的确如我们所推的那样,会发生截断。
2.4 位段的跨平台问题
1、int位段被当成有符号数还是无符号数是不确定的。
2、位段中最大位的数目不能确定(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题)。
3、位段中的成员在内存中从左向右分配,还是从右向左分配标识尚未定义。
4、当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,尚不确定。跟结构体相比,位段可以达到同样的效果,可以很好的节省空间,但是跨平台的问题存在。
2.5 使用位段需要注意的问题
1、冒号后面表示的是bit
2、冒号后面的数字(bit)不能超过类型的大小
3、如果不够,会直接新开辟,而不是会利用剩下的bit
4、位段开辟是以一次一个或者4个字节开辟的
5、位段会发生截断。
三.枚举
3.1 枚举的介绍
枚举,就是列举,比如我们生活中星期一到星期日可以一一列举,365天的每一天也可以一一列举。
简单的使用:
enum Day//星期 { Mon, Tues, Wed, Thur, Fri, Sat, Sun }; enum Sex//性别 { MALE, FEMALE, SECRET }; enum Color//颜色 { RED, GREEN, BLUE };
以上定义的enum Day,enum Sex,enum Color都是枚举类型。
{}中的内容是枚举类型的可能取值,也叫枚举常量。枚举默认第一个从0开始,一次递增1.当然在定义的时候可以赋初始值。比如:
enum Color { Red = 1, Green=2, Blue=5, yellow=7 };
这些都是可以的。
3.2 枚举的使用
3.3 枚举的优点
我们知道#define 可以定义,那么为什么还需要枚举呢?
1.增加代码的可读性和可维护性,比如我们在用switch语句设计分支时,如果用1,2,3,等等来设计选择,那么我们可能不知道意思是怎样的,如果用枚举来替代,代码可读性大大提高。
2.和#define定义的标识符比较,枚举是一种类型,有类型检查,更加严谨。
3.防止了命名污染。
4.便于调试。
5.使用方便,一次可以定义多个常量。
四.联合(共用体)
4.1 联合类型的定义
联合也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,但是特征是这些成员公用同一块空间(所以联合也叫共用体)。
这里我们发现它的大小是4,我们知道如果是按照结构体来说,大小应该是8,说明联合体的内存管理不同于结构体。
4.2联合的特点
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。
通过这串代码,我们发现它们的地址也是一模一样。这样我们就可以猜想,联合是共用一块空间地址也是一样的。那么联合所占空间大小如何计算呢?
4.3 联合大小的计算
1、联合的大小至少是最大成员的大小。
2、当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
比如:
在这里可能有些人可能疑惑第一个联合体Un1中char c[5]不是大小为5个字节,对齐数为5,第二哥联合体Un2中short c[7]不是大小为14个字节,对齐数为14吗?这样的话大小分别应该是5和14啊。这里我们要说一下,这里的两个数组本质上分别是5个char和7个short。所以对齐数还是1和2.
4.4 联合的应用
某度面试题:
设计一段程序,判断当前机器是大端存储还是小端存储。
普通实现:
int check_sys() { int m = 1; char n = (char)m;//强制转为char类型,如果是小端则 //返回1,大端则返回0 return n; } int main() { int ret=check_sys(); if (ret == 1) { printf("小端存储\n"); } else printf("大端存储\n"); return 0; }
联合体实现:
主要需要诸位注意的是遇到数组怎么判断对齐数。