第12周:文件、位运算
C
12.1.1 文件:格式化输入输出
格式化的输入输出
- printf
- %[flags][width][.prec][hlL]type
Flag |
含义 |
- |
左对齐 |
+ |
在前面放 + 或 - |
(space) |
正数留空 |
0 |
0填充 |
width或prec |
含义 |
number |
最小字符数 |
* |
下一个参数是字符数 |
.number |
小数点后的位数 |
.* |
下一个参数是小数点后的位数 |
类型修饰 |
含义 |
hh |
单个字节 |
h |
short |
l |
long |
ll |
long long |
L |
long double |
type |
用于 |
i或d |
int |
u |
unsigned int |
o |
八进制 |
x |
十六进制 |
X |
字母大写的十六进制 |
f或F |
float,6 |
e或E |
指数 |
g |
float |
G |
float |
a或A |
十六进制浮点 |
c |
char |
s |
字符串 |
P |
指针 |
n |
读入/写出的个数 |
flag |
含义 |
* |
跳过 |
数字 |
最大字符数 |
hh |
char |
h |
short |
l |
long, double |
ll |
long long |
L |
long double |
type |
用于 |
d |
int |
i |
整数,可能为十六进制或八进制 |
u |
unsigned int |
o |
八进制 |
x |
十六进制 |
X |
字母大写的十六进制 |
a,e,f,g |
float |
c |
char |
s |
字符串(单词) |
[^] |
所允许的字符 |
P |
指针 |
printf和scanf的返回值
- 读入的项目数
- 输出的字符数
- 在要求严格的程序中,应该判断每次调用scanf或printf的返回值,从而了解程序运行中是否存在问题
12.1.2 文件:文件输入输出
文件输入输出
FILE
- FILE* fopen(const char * restrict path, const char * restrict mode);
- int fclose(FILE *stream);
- fscanf(FILE*, ...)
- fprintf(FILE*, ...)
打开文件的标准代码
FILE* fp = fopen("file", "r");
if (fp) {
fscanf(fp,...);
fclose(fp);
} else {
...
}
fopen
r |
打开只读 |
r+ |
打开读写,从头文件开始 |
w |
打开只写。如果不存在则新建,如果存在则清空 |
w+ |
打开读写。如果不存在则新建,如果存在则清空 |
a |
打开追加。如果不存在则新建,如果存在则从文件尾开始 |
..X |
只新建,如果文件已存在则不能打开 |
12.1.3 文件:二进制文件
二进制文件
- 其实所有的文件最终都是二进制的
- 文本文件无非是用最简单的方式可以读写的文件
- 而二进制文件是需要专门的程序来读写的文件
- 文本文件的输入输出是格式化,可能经过转码
文本vs二进制
- Unix喜欢用文本文件来做数据存储和程序配置
- 交互式终端的出现使得人们喜欢用文本和计算机“talk”
- Unix的shell提供了一些读写文本的小程序
Windows喜欢用二进制文件
- DOS是草根文化,并不继承和熟悉Unix文化
- PC刚开始的时候能力有限,DOS的能力更有限,二进制更接近底层
文本的优势是方便人类读写,而且跨平台
- 文本的缺点是程序输入输出要经过格式化,开销大
- 二进制的缺点是人类读写困难,而且不跨平台
- 二进制的优点是程序读写快
程序为什么要文件
- 配置
- 数据
- 媒体
- 现实是,程序通过第三方库来读写文件,很少直接读写二进制文件了
可移植性
- 二进制文件不具有可移植性
- 在int为32位的机器上写成的数据文件无法直接在int为64位的机器上正确读出
- 解决方案之一是放弃使用int,而是typedef具有明确大小的类型
- 更好的方案是用文本
12.2.1 位运算:按位运算
按位运算
- C有这些按位运算的运算符:
- & 按位的与
- | 按位的或
- ~ 按位的反
- ^ 按位的异或
- << 左移
- >> 右移
按位与 &
- 如果 (x)i == 1 并且 (y)i == 1,那么 (x&y)i = 1
- 否则的话 (x&y)i = 0
- 按位与常用于两种应用:
- 让某一位或某些位为0:x & 0xFE
- 取一个数中的一段:x & 0xFF
按位或 |
- 如果 (x)i == 1 或 (y)i == 1,那么 (x | y)i = 1
- 否则的话 (x | y)i = 0
- 按位或常用于两种应用:
- 使得一位或几个位为1:x | 0x01
- 把两个数拼起来:0x00FF | 0XFF00
按位取反 ~
- (~x)i = 1 - (x)i
- 把1位变0,0位变1
- 想得到全部位为1的数:~0
- 7的二进制是0111,x | 7 使得低3位为1,而 x & ~7 ,就使得低三为位0
逻辑运算vs按位运算
- 对于逻辑运算,它只看到两个值:0和1
- 可以认为逻辑运算相当于把所有非0值都变成1,然后按位运算
- 5 & 4 -> 4 而 5 && 4 -> 1 & 1 -> 1
- 5 | 4 -> 5 而 5 || 4 -> 1 | 1 -> 1
- ~4 -> 3 而 !4 -> !1 -> 0
按位异或^
- 如果 (x)i == (y)i,那么 (x ^ y)i = 0
- 否则的话 (x ^ y)i = 1
- 如果两个位相等,那么结果为0,;不相等,结果为1
- 如果x和y相等,那么x^y的结果为0
- 对一个变量用同一个值异或两次,等于什么也没有做
12.2.2 位运算:移位运算
左移 <<
- i << j
- i中所有的位向左移动j个位置,而右边填入0
- 所有小于int的类型,移位以int的方式来做,结果小于int
- x <<= 1 等价于 x *= 2
- x <<= n 等价于 x *= 2^n
右移 >>
- i >> j
- i中所有的位向右移动j个位置
- 所有小于int的类型,移位以int的方式来做,结果是int
- 对于unsigned的类型,左边填入0
- 对于signed的类型,左边填入原来的最高位(保持符号不变)
- x >>= 1 等价于 x /= 2
- x >>= n 等价于 x /= 2^n
- 移位的位数不要用负数,这是没有定义的行为
12.2.3 位运算:位段
位段
struct {
unsigned int leading : 3;
unsigned int FLAG1 : 1;
unsigned int FLAG2 : 1;
int trailing : 11;
};
- 可以直接用位段的成员名称来访问
- 编译器会安排其中的位的排列,不具有可移植性
- 当所需的位超过一个int时会采用多个int