7.1 文件
7.1.1 格式化输入输出
格式化的输入输出
- printf
- %[flags][width][.prec][hIL]type
- scanf
- %[flag]type
printf和scanf都是有返回值的,printf返回读入的项目数,scanf返回输出的字符数,在要求严格的程序中,应该判断每次调用scanf或printf的返回值,从而了解程序运行中是否存在问题。
7.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{...
}
7.1.3 二进制文件
二进制文件
- 其实所有的文件最终都是二进制的;
- 文本文件无非是用最简单的方式可以读写的文件;
- more、tail;
- cat;
- vi;
- 而二进制文件是需要专门的程序来读写的文件;
- 文本文件的输入输出是格式化,可能经过转码;
文本和二进制相比,文本优势在于方便人类读写并且跨平台;缺点是程序输入输出需要经过格式化,开销大;二进制缺点是人类读写困难,不跨平台,比如int的大小不一致,大小端的问题等;优点是程序读写快。
程序为什么要文件
- 配置;
- Unix用文本,Windows用注册表;
- 数据;
- 稍微有点量的数据都放数据库了;
- 媒体;
- 这个只能是二进制的;
- 现实是,程序通过第三方库来读写文件,很少直接读写二进制文件了。
二进制读写
- size_t fread(void *restrict ptr,size_t size,size_t nitems,FILE *restrict stream);
- size_t fwrite(const void *restrict ptr,size_t size,size_t nitems,FILE *restrict stream);
- 注意FILE指针是最后一个参数;
- 返回的是成功读写的字节数;
在文件中定位
- long ftell(FILE *stream);
- int fseek(FILE *stream,long offset,int whence);
- SEEK_SET:从头开始;
- SEEK_CUR:从当前位置开始;
- SEEK_END :从尾开始(倒过来);
可移植性
- 这样的二进制文件不具有可移植性;
- 在int为32位的机器上写成的数据文件无法直接在int为64位的机器上正确读出;
- 解决方案之一是放弃使用int,而是typedef具有明确大小的类型;
- 更好的方案是用文本。
7.2 位运算
7.2.1 按位运算
按位运算
C有这些按位运算的运算符
- & 按位的与;
- |按位的或;
- ~按位取反;
- ^按位的异或;
- <<左移,>>右移;
按位与&
按位或|
按位取反~
逻辑运算VS按位运算
按位异或
7.2.2 移位运算
左移<<
右移>>
移位的位数不要用负数,这是没有定义的行为!
7.2.3 位运算例子
输出一个数的二进制
代码如下
#include <stdio.h>int main(int argc,char const argv[])
{int number;number=12345;unsigned mask=1u<<31;for(;mask;mask>>=1){printf("%d",number&mask?1:0);}printf("\n");return 0;
}
输出
00000000000000000011000000111001
位运算在单片机编程中的应用
7.2.4 位段
- 把一个int的若干位组合成一个结构;
struct{unsigned int leading:3;unsigned int FLAG1:1;unsigned int FLAG2:1;int trailing:11;
};
代码如下
#include <stdio.h>void prtBin(unsigned int number);struct U0{unsigned int leading:3;unsigned int FLAG1:1;unsigned int FLAG2:1;int trailing:27;
};int main(int argc,char const argv[])
{struct U0 uu;uu.leading=2;uu.FLAG1=0;uu.FLAG2=1;uu.trailing=0;printf("sizeof(uu)=%lu\n",sizeof(uu));prtBin(*(int*)&uu);return 0;
}void prtBin(unsigned int number)
{unsigned mask=1u<<31;for(;mask;mask>>=1){printf("%d",number&mask?1:0);}printf("\n");
}
输出
sizeof(uu)=4
00000000000000000000000000010010
位段
- 可以直接用位段的成员名称来访问;
- 比移位、与、或还方便;
- 编译器会安排其中的位的排列,不具有可移植性;
- 当所需的位超过一个int时会采用多个int;