#include <my_head.h>
int main(){
printf("11111");
while(1);
return 0;
}
结果程序没有任何输出。
1. 缓冲区的类型
1. 1 缓冲区的类型
- 行缓存----和终端相关就是行缓存 -- 最多可存1024字节
- 全缓存----和文件相关就是全缓存 -- 最多可存4096字节
- 无缓存----stderr
1.2 缓冲区的大小
行缓存 -- 1024
#include <my_head.h>
int main(int argc,const char *argv[]){
int a = 0;
fgetc(stdin);
printf("stdin-行缓存的大小是%ld\n",stdin->_IO_buf_end - stdin->_IO_buf_base);
return 0;
}
全缓存 -- 4096
#include <my_head.h>
int main(int argc,const char *argv[]){
FILE *fp = fopen("./b.c","w+");
if(NULL == fp){
PRINT_ERR("fopen error");
}
char ch = 0;
ch = fgetc(fp);//获取一个字符,文件中后面的4095个字符都会加载到缓冲区
printf("缓存的大小是%ld\n",fp->_IO_buf_end - fp->_IO_buf_base);
fclose(fp);
return 0;
}
运行结果:
无缓存 -- 0
#include <my_head.h>
int main(int argc,const char *argv[]){
printf("1");
perror("error");
while(1);
return 0;
}
1.3 缓冲区的刷新时机
- 程序结束会刷新
- 换行符会刷新缓冲区
- 输入输出切换会刷新缓冲区
- fflush会强制刷新
- 关闭FILE指针会刷新
- 缓冲区满会刷新
#include <my_head.h>
int main(int argc,const char *argv[]){
//1.程序结束会刷新
//printf("11111");
//2.换行符会刷新缓冲区
//printf("22222\n");
//while(1);
//3.输入输出切换会刷新缓冲区
//printf("333");
//int a = 0;
//scanf("%d",&a);
//while(1);
//4.fflush强制刷新
//printf("4444");
//fflush(stdout);//刷新标准输出缓冲区
//while(1);
//5.关闭FILE指针会刷新
//printf("5555");
//fclose(stdout);
//while(1);
//6.缓冲区满会刷新
for(int i = 0; i < 1025;i++){
fputs('a',stdout);
}
while(1);
return 0;
}
全缓存的刷新时机
- 程序结束会刷新
- 输入输出切换会刷新缓冲区
- fflush会强制刷新
- 关闭FILE指针会刷新
- 缓冲区满会刷新
2. 标准IO
2.1 fprintf/sprintf/snprintf 函数的使用
#include <stdio.h> -- 所需的头文件
int fprintf(FILE*stream,const char *format,...);
功能:向文件中格式化输出内容
参数:
stream:文件指针
format:输出格式(和printf一样)
...:可变参数,和printf一样
返回值:成功返回输出的字符的个数,失败返回负数
int sprintf(char *str,const char *format,...);
功能:向字符数组中格式化输出内容
参数:
str:字符数组的首地址
format:输出格式--和printf一样
...:可变参数
返回值:成功返回输出的字符个数,失败返回负数
int snprintf(char *str,size_t size,const char *format,...);
功能:向字符数组中格式化输出内容
参数:
str:字符数组的首地址
size:格式化输出的字符串的长度(size -1)
format:输出格式--和printf一样
...:可变参数
返回值:成功返回输出的字符个数,失败返回负数
fprintf/sprintf/snprintf函数的使用示例
#include <my_head.h>
int main(int argc,const char *argv[]){
//sprintf函数的使用
char str_buf[128] = {0};
char ch = 'a';
int var1 = 100;
char *str = "hello world";
sprintf(str_buf,"ch = %c,var1 = %d,str = %s",ch,var1,str);
printf("str_buf = [%s]\n",str_buf);
//snprintf和sprintf函数使用一致,但是snprintf可以解决容易数组越界的问题
char str_buf1[10] = {0};
snprintf(str_buf1,sizeof(str_buf1),"ch = %c,var1 = %d,str = %s",ch,var1,str);
printf("str_buf = [%s]\n",str_buf1);
//fprintf函数的使用
FILE *fp = fopen("./b.c","r+");
if(NULL == fp)
PRINT_ERR("fopen error");
fprintf(fp,"ch = %c",ch);
return 0;
}
运行结果:
2.2 fread/fwrite 函数的使用
#include <stdio.h>
size_t fread(void *ptr,size_t size,size_t nmemb,FILE *stream);
功能:从文件(stream)中读取nmemb*size个字节的数据到ptr
参数:
ptr:保存读取到的数据的首地址
size:块的大小
nmemb:块的数量
stream:文件指针
返回值:成功返回读取到的块的数量(如果size为1,读取到的块的数量和读取到的字节数是一样的);
失败或者读取到的文件结尾返回小于nmemb的数组,
int feof(FILE *stream);//如果读取到文件的结尾,这个函数会返回非0的数值
int ferror(FILE *stream);//如果读取错误,这个函数会返回非0的数值
if(feof(fp))
printf("读取到文件结尾\n");
if(ferror(fp))
printf("读取错误\n");
size_t fwrite(const void *ptr,size_t size,size_t nmemb,FILE *stream);
功能:从ptr中获取数据,向文件(stream)中写入nmemb*size个字节
参数:
ptr:保存读取到的数据的首地址
size:块的大小
nmemb:块的数量
stream:文件指针
返回值:成功返回写入块的数量,失败同上
2.3 fseek/ftell/rewind 光标相关的函数
#include <stdio.h> -- 所需要的头文件
int fseek(FILE *stream,long offset,int whence);
功能:偏移文件光标
参数:
stream:文件指针
offset:偏移量
大于0:向后偏移
等于0:不偏移
小于0:向前偏移
whence:偏移的基地址
SEEK_SET:从文件开头开始偏移
SEEK_CUR: 从当前位置开始偏移
SEEK_END: 从文件结尾开始偏移
返回值:成功返回0,失败返回-1,置位错误码
long ftell(FILE*stream);
功能:返回当前光标的位置
参数:文件指针
返回值:当前光标的位置
void rewind(FILE *stream);
功能:将光标置位到文件开头
偏移光标的使用示例
#include <my_head.h>
int main(int argc,const char *argv[]){
//打开文件
FILE *fp = fopen("./b.c","r+");
if(NULL == fp){
PRINT_ERR("fopen error");
}
//从当前位置向后偏移4字节
fseek(fp,4,SEEK_CUR);
int ch = fgetc(fp);
printf("ch = %c\n",ch);
//从文件结尾向前偏移
fseek(fp,-4,SEEK_END);
ch = getc(fp);
printf("ch = %c\n",ch);
//从文件开头向后偏移
fseek(fp,10,SEEK_SET);
ch = fgetc(fp);
printf("ch = [%c]\n",ch);
//使用ftell获取当前光标的位置
int pos = ftell(fp);
printf("当前光标位置:%d\n",pos);
return 0;
}
3.4 使用标准IO对bmp图片进行操作
三原色:每个像素中包含R、G、B三种颜色;
不同颜色的亮度位0~255
对于图片来说,每个像素点可以理解为由三个255的数据组成。
一个像素点占三个字节。
3、文件IO(系统调用)
3.1 文件IO的特点
文件IO又叫体统调用。
系统调用:就是受控的内核入口,我们通过系统调用和内核进行交互。
文件IO的可移植性差,效率低,实时性强。
3.2 文件描述符
在文件IO中,我们通过文件描述符去操作文件。
文件描述符本质是一个大于等于0的整数,使用open函数获取文件描述符。
每个正在运行的程序,可以拥有的文件描述符最大个数为1024,ulimit -n 2048 可以修改这个限制
每个正在运行的程序都会有三个默认打开的文件描述符。
标准输入:0
标准输出:1
标准出错:2
FILE指针和文件描述符的关系:
FILE指针包含文件描述符。
3.3 open(打开文件)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> ------所需头文件
int open(const char *pathname,int flags);
int open(const char *pathname,int flags,mode_t mode);
功能:打开文件
参数:
pathname:要打开的文件的路径和名字
flags:打开文件的模式
O_RDONLY:只读方式打开
O_WRONLY: 只写方式打开
O_RDWR: 读写方式打开文件
-----------------------以上三个参数必须有一个-----------------
O_APPEND: 追加方式打开文件
O_CREAT: 创建的方式打开文件,用这个必须加上第三个参数
O_TRUNC:清空文件
O_EXCL: 结合O_CREAT使用,如果文件存在会置为EEXIST错误码
mode:如果flags加上O_CREAT 选项,就需要使用此参数
创建的文件权限:如0666
实际创建的文件权限是0666 & (~umask)
umask是创建文件的掩码,通过umask可以查看掩码 umask 0222 修改掩码
返回值:成功返回文件描述符(遵循最小分配原则),失败返回-1,置位错误码
使用方式:
open("./a.txt",O_RDWR|O_CREAT|O_TRUNC,0666);
r+ O_RDWR
W: O_WRONLY|O_CREAT|O_TRUNC
flags:原理,本质就是位操作
flags:这个参数的每个bit都会对应一种打开文件的模式
#include <my_head.h>
int main(int argc,const char *argv[]){
//打开文件
int fd;
fd = open("./b.c",O_RDONLY);
if(-1 == fd){
PRINT_ERR("open error");
}
printf("打开文件成功,文件描述符为%d\n",fd);
return 0;
}
运行结果: