五十一、枚举类型
51.1、定义
枚举是 C 语言中的一种基本数据类型,它可以让数据更简洁,更易读
enum 枚举名 {枚举元素1,枚举元素2,……};
如果一个变量只有几种可能的值,那么就可以将其定义为枚举类型。
-
如果不用枚举,我们需要使用 #define 来为每个整数定义一个别名
-
枚举类型是被当做 int 或者 unsigned int 类型来处理的——不会是其他数据类型!!!
-
所以按照 C 语言规范是没有办法遍历枚举类型的
-
特殊情况:用 for 来遍历枚举的元素【只是赋值了一个整型类型的值】必须连续!!!
-
第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1
可以在定义枚举类型时改变枚举元素的值:
enum season {spring, summer=3, autumn, winter};
没有指定值的枚举元素,其值为前一元素加 1
也就说 spring 的值为 0,summer 的值为 3,autumn 的值为 4,winter 的值为 5
- 枚举类型无法修改
51.2、枚举变量的定义
1、先定义枚举类型,再定义枚举变量
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY day;
2、定义枚举类型的同时定义枚举变量
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
3、省略枚举名称,直接定义枚举变量
enum
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
五十二、位域
52.1、位域
有些信息在存储时,并不需要占用一个完整的字节
而只需占几个或一个二进制位
例如在存放一个开关量时,只有 0 和 1 两种状态,用 1 位二进位即可
为了节省存储空间,并使处理简便
C 语言又提供了一种数据结构,称为"位域"或"位段
是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数
每个域有一个域名
位域在本质上就是一种结构类型,不过其成员是按二进位分配的
带有预定义宽度的变量被称为位域
52.2、如何定义
- 使用位域的做法是在结构体定义时,在结构体成员后面使用冒号(:)和数字来表示该成员所占的位数
- 位域的宽度不能超过它所依附的数据类型长度。比如上面a位域长度不能超过32位,因为int占四个字节
52.3、无名位域
- 位域成员可以没有名称,只要给出数据类型和位宽即可,可以用来隔离空间、调整位置、填充
52.4、补充
一个位域存储在同一个字节中,如一个字节所剩空间不够存放另一位域时,则会从下一单元起存放该位域
也可以有意使某位域从下一单元开始
struct bs{
unsigned a:4;
unsigned :4; /* 空域 */
unsigned b:4; /* 从下一单元开始存放 */
unsigned c:4
}
在这个位域定义中,a 占第一字节的 4 位,后 4 位填 0 表示不使用,b 从第二字节开始,占用 4 位,c 占用 4 位
由于位域不允许跨两个字节
因此位域的长度不能大于一个字节的长度
也就是说不能超过8位二进位
如果最大长度大于计算机的整数字长,一些编译器可能会允许域的内存重叠
另外一些编译器可能会把大于一个域的部分存储在下一个字中
52.5、位域的定义和位域变量的说明
struct 位域结构名
{
位域列表
};
//其中位域列表的形式为:
类型说明符 位域名: 位域长度
举例:
struct bs{
int a:8;
int b:2;
int c:6;
}data;
data 为 bs 变量,共占两个字节
其中位域 a 占 8 位,位域 b 占 2 位,位域 c 占 6 位
52.6、位域的使用
位域变量名.位域名
位域变量名->位域名
main(){
struct bs{
unsigned a:1;
unsigned b:3;
unsigned c:4;
} bit,*pbit;
bit.a=1; /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
bit.b=7; /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
bit.c=15; /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
printf("%d,%d,%d\n",bit.a,bit.b,bit.c); /* 以整型量格式输出三个域的内容 */
pbit=&bit; /* 把位域变量 bit 的地址送给指针变量 pbit */
pbit->a=0; /* 用指针方式给位域 a 重新赋值,赋为 0 */
pbit->b&=3; /* 使用了复合的位运算符 "&=",相当于:pbit->b=pbit->b&3,位域 b 中原有值为 7,与 3 作按位与运算的结果为 3(111&011=011,十进制值为 3) */
pbit->c|=1; /* 使用了复合位运算符"|=",相当于:pbit->c=pbit->c|1,其结果为 15 */
printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c); /* 用指针方式输出了这三个域的值 */
}
上例程序中定义了位域结构 bs
三个位域为 a、b、c
说明了 bs 类型的变量 bit 和指向 bs 类型的指针变量 pbit。这表示位域也是可以使用指针的
52.7、位域声明
struct
{
type [member_name] : width ;
};
元素 | 描述 |
---|---|
type | 只能为 int(整型) unsigned int(无符号整型) signed int(有符号整型) 三种类型,决定了如何解释位域的值 |
member_name | 位域的名称 |
width | 位域中位的数量 宽度必须小于或等于指定类型的位宽度 |
位域可以存储多于 1 位的数
#include <stdio.h>
#include <string.h>
struct
{
unsigned int age : 3;
} Age;
int main( )
{
Age.age = 4;
printf( "Sizeof( Age ) : %d\n", sizeof(Age) );
printf( "Age.age : %d\n", Age.age );
Age.age = 7;
printf( "Age.age : %d\n", Age.age );
Age.age = 8; // 二进制表示为 1000 有四位,超出
printf( "Age.age : %d\n", Age.age );
return 0;
}
运行结果:
Sizeof( Age ) : 4
Age.age : 4
Age.age : 7
Age.age : 0
五十三、位操作
53.1、逻辑位运算符
53.2、按位取反(~)
逻辑位运算符中优先级中最高的是按位取反运算符,它的运算符是一个~符号,作用是将1变成0,将0变成1
53.3、按位与(&)
优先级第二高的是按位与运算符,它的运算符是一个&符号(而逻辑与是两个&符号)
53.4、按位异或(^)
优先级排第三的是按位异或运算符,它的运算符是一个^符号,只有当两个操作数对应的二进制位不同时,它的结果才为1,否则为0
53.5、按位或(|)
逻辑位运算符中优先级最低的是按位或运算符,它的运算符是一个|符号(而逻辑或是两个|符号)
53.6、和赋值号结合
这四个运算符,除了按位取反只有一个操作数之外,其他三个都可以跟赋值号(=)结合到一块,使得代码更加简洁
五十四、移位和位操作的应用
54.1、移位运算符
C语言除了提供四种逻辑位运算符之外,还提供了可以将某个变量中所有的二进制位左移或右移的运算符——移位运算符
54.2、左移位运算符
11001010 << 2
54.3、右移位运算符
11001010 >> 2
54.4、和赋值号结合
左移、右移运算符也可以和赋值号结合…
54.5、一些未定义行为
左移、右移运算符右边的操作数如果是为负数,或者右边的操作数大于左边操作数支持的最大宽度,那么表达式的结果均是属于“未定义行为”。
54.6、应用
- 掩码
子网掩码 - 打开位
- 关闭位
- 转置位
五十五、打开和关闭文件
55.1、程序执行流程
55.2、文件是什么
55.3、文本文件和二进制文件
55.4、打开和关闭文件
五十六、读写文件
56.1、读写单个字符
56.2、读写整个字符串
56.3、格式化读写文件
56.4、二进制读写文件
56.5、随机读写文件
获取当前读写位置->ftell
修改读写位置->rewind
fseek
fseek
56.6、可移植性问题
五十七、标准流和错误处理
57.1、标准流
57.2、重定向
57.3、错误处理
五十八、IO缓冲区
58.1、IO缓冲区
可以使用setvbuf修改缓冲模式
setvbuf
58.2、fflush
刷新缓冲区,将缓冲区数据立刻存放到目标位置