声明: 本篇博客的学习途径主要为以下网站和课堂讲解,发博客目的仅为学习使用,在该博客的基础上做了一定程序的简略和修改。
参考博客 :
原文链接:http://c.biancheng.net/c/
文件
在操作系统中,为了统一对各种硬件的操作,简化接口,不同的硬件设备也都被看成一个文件。对这些文件的操作,等同于对磁盘上普通文件的操作。例如:
- 通常把显示器称为标准输出文件,printf 就是向这个文件输出数据;
- 通常把键盘称为标准输入文件,scanf 就是从这个文件读取数据。
文件 | 硬件设备 |
---|---|
stdin | 标准输入文件,一般指键盘;scanf()、getchar() 等函数默认从 stdin 获取输入。 |
stdout | 标准输出文件,一般指显示器;printf()、putchar() 等函数默认向 stdout 输出数据。 |
stderr | 标准错误文件,一般指显示器;perror() 等函数默认向 stderr 输出数据(后续会讲到)。 |
stdprn | 标准打印文件,一般指打印机。 |
操作文件的正确流程为:打开文件 --> 读写文件 --> 关闭文件
文件流:数据文件和内存之间传递的过程
【文件流】
数据在文件和内存之间传递的过程叫做文件流
- 输入流:数据从文件复制到内存的过程
- 输出流:从内存保存到文件的过程
【数据流】
文件是数据源的一种,除了文件,还有数据库、网络、键盘等;
我们把数据在数据源和程序(内存)之间传递的过程叫做数据流(Data Stream)
- 输入流(Input Stream):数据从数据源到程序(内存)的过程
- 输出流(Output Stream):从程序(内存)到数据源的过程叫做输出流
- 输入输出(Input output,IO):是指程序(内存)与外部设备(键盘、显示器、磁盘、其他计算机等)进行交互的操作
fopen() 函数打开文件
【注意】
标准输入文件 stdin(表示键盘)、标准输出文件 stdout(表示显示器)、标准错误文件 stderr(表示显示器)是由系统打开的,可直接使用。
fopen() 头文件:<stdio.h>
FILE *fopen(char *filename, char *mode);
- ilename为文件名(包括文件路径)
- mode为打开方式
它们都是字符串
f
o
p
e
n
(
)
函
数
的
返
回
值
fopen() 函数的返回值
fopen()函数的返回值
fopen() 会获取文件信息,包括文件名、文件状态、当前读写位置等,并将这些信息保存到一个 FILE 类型的结构体变量中,然后将该变量的地址返回。
fopen()返回值
- 返回成功 : 返回地址
- 返回失败:返回空指针NULL
【举例】
如果希望接收 fopen() 的返回值,就需要定义一个 FILE 类型的指针
FILE *fp = fopen("demo.txt", "r");
表示以“只读”方式打开当前目录下的 demo.txt 文件
并使 fp 指向该文件,这样就可以通过 fp 来操作 demo.txt 了
fp:文件指针
判
断
文
件
是
否
打
开
成
功
判断文件是否打开成功
判断文件是否打开成功
打开文件出错时,fopen() 将返回一个空指针,也就是 NULL,我们可以利用这一点来判断文件是否打开成功
FILE *fp;
if( (fp=fopen("D:\\demo.txt","rb")) == NULL ){
printf("Fail to open file!\n");
exit(0); //退出程序(结束程序)
}
文件打开方式
在调用 fopen() 函数时,需要提供文件打开方式
打开方式 | 说明 |
---|---|
“r” | 以“只读”方式打开文件。只允许读取,不允许写入。文件必须存在,否则打开失败。 |
“w” | 以“写入”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。 |
“a” | 以“追加”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留) |
“r+” | 以“读写”方式打开文件。既可以读取也可以写入,也就是随意更新文件。文件必须存在,否则打开失败。 |
“w+” | 以“写入/更新”方式打开文件,相当于w和r+叠加的效果。 |
“a+” | 以“追加/更新”方式打开文件,相当于a和r+叠加的效果。 |
“t” | 文本文件。如果不写,默认为"t"。 |
“b” | 二进制文件 |
上半部属于读写权限,下半部属于读写方式
【注意】不能将 读写方式 放在 读写权限 的开头
字符含义:
- r(read):读
- w(write):写
- a(append):追加
- t(text):文本文件
- b(binary):二进制文件
- +:读和写
fclose() 关闭文件
文件一旦使用完毕,应该用 fclose() 函数把文件关闭,以释放相关资源,避免数据丢失。
int fclose(FILE *fp);
- 文件正常关闭时,fclose() 的返回值为0
- 如果返回非零值则表示有错误发生。
【举例】
#include <stdio.h>
#include <stdlib.h>
#define N 100
int main() {
FILE *fp;
char str[N + 1];
//判断文件是否打开失败
if ( (fp = fopen("d:\\demo.txt", "rt")) == NULL ) {
puts("Fail to open file!");
exit(0);
}
//循环读取文件的每一行数据
while( fgets(str, N, fp) != NULL ) {
printf("%s", str);
}
//操作结束后关闭文件
fclose(fp);
return 0;
}
fgetc/fputc字符形式读写文件
在C语言中,读写文件比较灵活,
- 既可以每次读写一个字符,
- 也可以读写一个字符串,
- 甚至是任意字节的数据(数据块)
每次可以从文件中读取一个字符,或者向文件中写入一个字符。主要使用两个函数,分别是 fgetc() 和 fputc()。
字符读取函数 fgetc()
fgetc 是 file get char的缩写 : 从指定的文件中读取一个字符
int fgetc (FILE *fp);
- fgetc() 读取成功时返回读取到的字符
- 读取到文件末尾或读取失败时返回EOF( end of file),它的值是一个负数,往往是 -1。
【实例】
char ch;
FILE *fp = fopen("D:\\demo.txt", "r+");
ch = fgetc(fp);
表示从D:\demo.txt文件中读取一个字符,并保存到变量 ch 中。
在文件内部有一个位置指针,用来指向当前读写到的位置,也就是读写到第几个字节。在文件打开时,该指针总是指向文件的第一个字节。使用 fgetc() 函数后,该指针会向后移动一个字节,所以可以连续多次使用 fgetc() 读取多个字符。
【注意】:这个文件内部的位置指针与C语言中的指针不是一回事。位置指针仅仅是一个标志,表示文件读写到的位置,也就是读写到第几个字节,它不表示地址。
文件每读写一次,位置指针就会移动一次,它不需要你在程序中定义和赋值,而是由系统自动设置,对用户是隐藏的。
字符写入函数 fputc()
fputc : file output char 向指定的文件中写入一个字符
int fputc ( int ch, FILE *fp );
- ch 为要写入的字符
- fp 为文件指针
- fputc() 写入成功时返回写入的字符,失败时返回 EOF
【例】
fputc('a', fp);
【例】
char ch = 'a';
fputc(ch, fp);
【例】从键盘输入一行字符,写入文件。
#include<stdio.h>
int main(){
FILE *fp;
char ch;
//判断文件是否成功打开
if( (fp=fopen("D:\\demo.txt","wt+")) == NULL ){
puts("Fail to open file!");
exit(0);
}
printf("Input a string:\n");
//每次从键盘读取一个字符并写入文件
while ( (ch=getchar()) != '\n' ){
fputc(ch,fp);
}
fclose(fp);
return 0;
}
feof() 和 ferror()判断结束类型
【问题】EOF 本来表示文件末尾,意味着读取结束,但是很多函数在读取出错时也返回 EOF,那么当返回 EOF 时,到底是文件读取完毕了还是读取出错了?
我们可以借助 stdio.h 中的两个函数来判断,分别是 feof() 和 ferror()。
- feof判断是否是文件结束
- 当指向文件末尾时返回非零值
- 否则返回零值。
feof() 函数用来判断文件内部指针是否指向了文件末尾
int feof ( FILE * fp );
- ferror()判断文件操作是否出错
- 出错时返回非零值
- 否则返回零值。
int ferror ( FILE *fp );
fgets()/fputs 字符串 形式读写文件
fgetc() 和 fputc() 函数每次只能读写一个字符,速度较慢;实际开发中往往是每次读写一个字符串或者一个数据块
fgets() 读字符串
fgets() 函数用来从指定的文件中读取一个字符串,并保存到 字符数组 中
char *fgets ( char *str, int n, FILE *fp );
- str 为字符数组
- n 为要读取的字符数目
- fp 为文件指针。
- 返回值:
- 读取成功时返回字符数组首地址,也即 str;
- 读取失败时返回 NULL;
【注意】,读取到的字符串会在末尾自动添加 ‘\0’,n 个字符也包括 ‘\0’。
也就是说,实际只读取到了 n-1 个字符,如果希望读取 100 个字符,n 的值应该为 101。
#define N 101
char str[N];
FILE *fp = fopen("D:\\demo.txt", "r");
fgets(str, N, fp);
表示从 D:\\demo.txt 中读取 100 个字符,并保存到字符数组 str 中
【注意】在读取到 n-1 个字符之前如果出现了换行,或者读到了文件末尾,则读取结束。这就意味着,不管 n 的值多大,fgets() 最多只能读取一行数据,不能跨行。
【实例】
#include <stdio.h>
#include <stdlib.h>
#define N 100
int main(){
FILE *fp;
char str[N+1];
if( (fp=fopen("d:\\demo.txt","rt")) == NULL ){
puts("Fail to open file!");
exit(0);
}
while(fgets(str, N, fp) != NULL){
printf("%s", str);
}
fclose(fp);
return 0;
}
结果:
将输入内容复制到 D:\\demo.txt
fgets() 遇到换行时,会将换行符一并读取到当前字符串。该示例的输出结果之所以和 demo.txt 保持一致,该换行的地方换行,就是因为 fgets() 能够读取到换行符。
而 gets() 不一样,它会忽略换行符。
fputs() 写字符串
int fputs( char *str, FILE *fp );
- str 为要写入的字符串
- fp 为文件指针。
- 写入成功返回非负数,失败返回 EOF
【例】
char *str = "http://c.biancheng.net";
FILE *fp = fopen("D:\\demo.txt", "at+");
fputs(str, fp);
表示把把字符串 str 写入到 D:\demo.txt 文件中。\
【实例】向上例中建立的 d:\demo.txt 文件中追加一个字符串。
#include<stdio.h>
int main(){
FILE *fp;
char str[102] = {0}, strTemp[100];
if( (fp=fopen("D:\\demo.txt", "at+")) == NULL ){
puts("Fail to open file!");
exit(0);
}
printf("Input a string:");
gets(strTemp);
strcat(str, "\n");
strcat(str, strTemp);
fputs(str, fp);
fclose(fp);
return 0;
}
fread和fwrite 数据块 的形式读写文件
对于 Windows 系统,使用 fread() 和 fwrite() 时应该以二进制的形式打开文件 , Linux平台没有要求
所谓块数据,也就是若干个字节的数据,可以是一个字符,可以是一个字符串,可以是多行数据,并没有什么限制
- fread() 函数用来从指定文件中读取块数据
size_t fread ( void *ptr, size_t size, size_t count, FILE *fp );
- fwrite() 函数用来向文件中写入块数据
size_t fwrite ( void * ptr, size_t size, size_t count, FILE *fp );
- ptr 为内存区块的指针,它可以是数组、变量、结构体等。fread() 中的 ptr 用来存放读取到的数据,
- fwrite() 中的 ptr 用来存放要写入的数据。
- size:表示每个数据块的字节数。
- count:表示要读写的数据块的块数。
- fp:表示文件指针。
- 理论上,每次读写 size*count 个字节的数据。
size_t 是在 stdio.h 和 stdlib.h 头文件中使用 typedef 定义的数据类型,表示无符号整数,也即非负数,常用来表示数量。
返回值:返回成功读写的块数,也即 count。如果返回值小于 count:
- 对于 fwrite() 来说,肯定发生了写入错误,可以用 ferror() 函数检测。
- 对于 fread() 来说,可能读到了文件末尾,可能发生了错误,可以用 ferror() 或 feof() 检测。
【实例】从键盘输入一个数组,将数组写入文件再读取出来。
#include<stdio.h>
#define N 5
int main(){
//从键盘输入的数据放入a,从文件读取的数据放入b
int a[N], b[N];
int i, size = sizeof(int);
FILE *fp;
if( (fp=fopen("D:\\demo.txt", "rb+")) == NULL ){ //以二进制方式打开
puts("Fail to open file!");
exit(0);
}
//从键盘输入数据 并保存到数组a
for(i=0; i<N; i++){
scanf("%d", &a[i]);
}
//将数组a的内容写入到文件
fwrite(a, size, N, fp);
//将文件中的位置指针重新定位到文件开头
rewind(fp);
//从文件读取内容并保存到数组b
fread(b, size, N, fp);
//在屏幕上显示数组b的内容
for(i=0; i<N; i++){
printf("%d ", b[i]);
}
printf("\n");
fclose(fp);
return 0;
}
运行结果:
23 409 500 100 222↙
23 409 500 100 222
打开 D:\demo.txt,发现文件内容根本无法阅读。这是因为我们使用"rb+"方式打开文件,数组会原封不动地以二进制形式写入文件,一般无法阅读。
数据写入完毕后,位置指针在文件的末尾,要想读取数据,必须将文件指针移动到文件开头,这就是rewind(fp);的作用
【示例】从键盘输入两个学生数据,写入一个文件中,再读出这两个学生的数据显示在屏幕上。
#include<stdio.h>
#define N 2
struct stu{
char name[10]; //姓名
int num; //学号
int age; //年龄
float score; //成绩
}boya[N], boyb[N], *pa, *pb;
int main(){
FILE *fp;
int i;
pa = boya;
pb = boyb;
if( (fp=fopen("d:\\demo.txt", "wb+")) == NULL ){
puts("Fail to open file!");
exit(0);
}
//从键盘输入数据
printf("Input data:\n");
for(i=0; i<N; i++,pa++){
scanf("%s %d %d %f",pa->name, &pa->num,&pa->age, &pa->score);
}
//将数组 boya 的数据写入文件
fwrite(boya, sizeof(struct stu), N, fp);
//将文件指针重置到文件开头
rewind(fp);
//从文件读取数据并保存到数据 boyb
fread(boyb, sizeof(struct stu), N, fp);
//输出数组 boyb 中的数据
for(i=0; i<N; i++,pb++){
printf("%s %d %d %f\n", pb->name, pb->num, pb->age, pb->score);
}
fclose(fp);
return 0;
}
Input data:
Tom 2 15 90.5↙
Hua 1 14 99↙
Tom 2 15 90.500000
Hua 1 14 99.000000
fscanf和fprintf 对磁盘进行读写 (格式化读写)
- fscanf() 和 fprintf() 函数:对磁盘进行读写
- scanf() 和 printf() 函数:对键盘和显示器进行读写显示
int fscanf ( FILE *fp, char * format, ... );
int fprintf ( FILE *fp, char * format, ... );
- fp 为文件指针
- format 为格式控制字符串
- … 表示参数列表。
FILE *fp;
int i, j;
char *str, ch;
fscanf(fp, "%d %s", &i, str);
fprintf(fp,"%d %c", j, ch);
【返回值】:
- fprintf() 返回
- 成功写入的字符的个数
- 失败则返回负数。
- fscanf() 返回参数列表中被成功赋值的参数个数。
【示例】用 fscanf 和 fprintf 函数来完成对学生信息的读写。
#include<stdio.h>
#define N 2
struct stu{
char name[10];
int num;
int age;
float score;
} boya[N], boyb[N], *pa, *pb;
int main(){
FILE *fp;
int i;
pa=boya;
pb=boyb;
if( (fp=fopen("D:\\demo.txt","wt+")) == NULL ){
puts("Fail to open file!");
exit(0);
}
//从键盘读入数据,保存到boya
printf("Input data:\n");
for(i=0; i<N; i++,pa++){
scanf("%s %d %d %f", pa->name, &pa->num, &pa->age, &pa->score);
}
pa = boya;
//将boya中的数据写入到文件
for(i=0; i<N; i++,pa++){
fprintf(fp,"%s %d %d %f\n", pa->name, pa->num, pa->age, pa->score);
}
//重置文件指针
rewind(fp);
//从文件中读取数据,保存到boyb
for(i=0; i<N; i++,pb++){
fscanf(fp, "%s %d %d %f\n", pb->name, &pb->num, &pb->age, &pb->score);
}
pb=boyb;
//将boyb中的数据输出到显示器
for(i=0; i<N; i++,pb++){
printf("%s %d %d %f\n", pb->name, pb->num, pb->age, pb->score);
}
fclose(fp);
return 0;
}
结果:
Input data:
Tom 2 15 90.5↙
Hua 1 14 99↙
Tom 2 15 90.500000
Hua 1 14 99.000000
打开 D:\demo.txt,发现文件的内容是可以阅读的,格式非常清晰。
用 fprintf() 和 fscanf() 函数读写配置文件、日志文件会非常方便,不但程序能够识别,用户也可以看懂,可以手动修改。
如果将 fp 设置为 stdin,那么 fscanf() 函数将会从键盘读取数据,与 scanf 的作用相同;
设置为 stdout,那么 fprintf() 函数将会向显示器输出内容,与 printf 的作用相同
#include<stdio.h>
int main(){
int a, b, sum;
fprintf(stdout, "Input two numbers: ");
fscanf(stdin, "%d %d", &a, &b);
sum = a + b;
fprintf(stdout, "sum=%d\n", sum);
return 0;
}
运行结果:
Input two numbers: 10 20↙
sum=30
rewind和fseek改变位置指针位置(随机读写)
随机读写:在实际开发中经常需要读写文件的中间部分,要解决这个问题,就得先移动文件内部的位置指针,再进行读写
实现随机读写的关键是要按要求移动位置指针,这称为文件的定位。
- rewind() 用来将位置指针移动到文件开头
void rewind ( FILE *fp );
- fseek() 用来将位置指针移动到任意位置
int fseek ( FILE *fp, long offset, int origin );
【参数说明】
- fp 为文件指针,也就是被移动的文件。
- offset 为偏移量,也就是要移动的字节数。之所以为 long 类型,是希望移动的范围更大,能处理的文件更大。offset 为正时,向后移动;offset 为负时,向前移动。
- origin 为起始位置,也就是从何处开始计算偏移量。
C语言规定的起始位置有三种,分别为文件开头、当前位置和文件末尾
起始点 | 常量名 | 常量值 |
---|---|---|
文件开头 | SEEK_SET | 0 |
当前位置 | SEEK_CUR | 1 |
文件末尾 | SEEK_END | 2 |
【例】把位置指针移动到离文件开头100个字节处:
fseek(fp, 100, 0);
值得说明的是,fseek() 一般用于二进制文件,在文本文件中由于要进行转换,计算的位置有时会出错。
文 件 的 随 机 读 写 文件的随机读写 文件的随机读写
在移动位置指针之后,就可以用前面介绍的任何一种读写函数进行读写了
由于是二进制文件,因此常用 fread() 和 fwrite() 读写。
【例】从键盘输入三组学生信息,保存到文件中,然后读取第二个学生的信息。
#include<stdio.h>
#define N 3
struct stu{
char name[10]; //姓名
int num; //学号
int age; //年龄
float score; //成绩
}boys[N], boy, *pboys;
int main(){
FILE *fp;
int i;
pboys = boys;
if( (fp=fopen("d:\\demo.txt", "wb+")) == NULL ){
printf("Cannot open file, press any key to exit!\n");
getch();
exit(1);
}
printf("Input data:\n");
for(i=0; i<N; i++,pboys++){
scanf("%s %d %d %f", pboys->name, &pboys->num, &pboys->age, &pboys->score);
}
fwrite(boys, sizeof(struct stu), N, fp); //写入三条学生信息
fseek(fp, sizeof(struct stu), SEEK_SET); //移动位置指针
fread(&boy, sizeof(struct stu), 1, fp); //读取一条学生信息
printf("%s %d %d %f\n", boy.name, boy.num, boy.age, boy.score);
fclose(fp);
return 0;
}
Input data:
Tom 2 15 90.5↙
Hua 1 14 99↙
Zhao 10 16 95.5↙
Hua 1 14 99.000000
ftell()函数 获取位置指针距离文件开头的字节数 - 获取文件的大小
long int ftell ( FILE * fp );
【注意】fp 要以二进制方式打开,如果以文本方式打开,函数的返回值可能没有意义
【例】先使用 fseek() 将文件内部指针定位到文件末尾,再使用 ftell() 返回内部指针距离文件开头的字节数,这个返回值就等于文件的大小
long fsize(FILE *fp){
fseek(fp, 0, SEEK_END);
return ftell(fp);
}
【问题】这段代码并不健壮,它移动了文件内部指针,可能会导致接下来的文件操作错误。导致fread() 函数将永远读取不到内容。
long size = fsize(fp);
fread(buffer, 1, 1, fp);
【结论】获取到文件大小后还需要恢复文件内部指针
【例】
long fsize(FILE *fp){
long n;
fpos_t fpos; //当前位置
fgetpos(fp, &fpos); //获取当前位置
fseek(fp, 0, SEEK_END);
n = ftell(fp);
fsetpos(fp,&fpos); //恢复之前的位置
return n;
}
fpos_t 是在 stdio.h 中定义的结构体,用来保存文件的内部指针。
fgetpos() 用来获取文件内部指针
fsetpos() 用来设置文件内部指针。
【实例】完整示例
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
long fsize(FILE *fp);
int main(){
long size = 0;
FILE *fp = NULL;
char filename[30] = "D:\\1.mp4";
if( (fp = fopen(filename, "rb")) == NULL ){ //以二进制方式打开文件
printf("Failed to open %s...", filename);
getch();
exit(EXIT_SUCCESS);
}
printf("%ld\n", fsize(fp));
return 0;
}
long fsize(FILE *fp){
long n;
fpos_t fpos; //当前位置
fgetpos(fp, &fpos); //获取当前位置
fseek(fp, 0, SEEK_END);
n = ftell(fp);
fsetpos(fp,&fpos); //恢复之前的位置
return n;
}
FILE结构体
文件指针
文件指针:用于指向一个文件的指针
通过文件指针就可对它所指的文件进行各种操作。
文件指针的格式:
FILE *fp;
FILE结构体
FILE,实际上是在stdio.h中定义的一个结构体,该结构体中含有文件名、文件状态和文件当前位置等信息
【注意】:FILE是文件缓冲区的结构,fp也是指向文件缓冲区的指针。
不同编译器 stdio.h 头文件中对 FILE 的定义略有差异,这里以标准C举例说明:
【例】
typedef struct _iobuf {
int cnt; // 剩余的字符,如果是输入缓冲区,那么就表示缓冲区中还有多少个字符未被读取
char *ptr; // 下一个要被读取的字符的地址
char *base; // 缓冲区基地址
int flag; // 读写状态标志位
int fd; // 文件描述符
// 其他成员
} FILE;
控制缓冲区
当我们从键盘输入数据的时候,数据并不是直接被我们得到,而是放在了缓冲区中,然后我们从缓冲区中得到我们想要的数据
【问题】如果我们通过setbuf()或setvbuf()函数将缓冲区设置10个字节的大小,而我们从键盘输入了20个字节大小的数据,这样我们输入的前10个数据会放在缓冲区中,因为我们设置的缓冲区的大小只能够装下10个字节大小的数据,装不下20个字节大小的数据。那么剩下的那10个字节大小的数据怎么办呢?
答案: 暂时放在了输入流中。
上面的箭头表示的区域就相当是一个输入流,红色的地方相当于一个开关,这个开关可以控制往深绿色区域(标注的是缓冲区)里放进去的数据,输入20个字节的数据只往缓冲区中放进去了10个字节,剩下的10个字节的数据就被停留在了输入流里!
【问题】系统是如何来控制这个缓冲区呢?
与FILE结构体的几个成员有关
cnt // 剩余的字符,如果是输入缓冲区,那么就表示缓冲区中还有多少个字符未被读取
ptr // 下一个要被读取的字符的地址
base // 缓冲区基地址
缓冲区的基地址 base 是0x00428e60
我们向缓冲区中放入了10个字节大小的数据
- FILE结构体中的 cnt 变为了10(说明此时缓冲区中有10个字节大小的数据可以读)
- 此时 ptr 的值也为0x00428e60 (表示从0x00428e60这个位置开始读取数据)
当我们从缓冲区中读取5个数据的时候
- cnt 变为了5(表示缓冲区还有5个数据可以读)
- ptr 则变为了0x0042e865(表示下次应该从这个位置开始读取缓冲区中的数据)
接下来我们再读取5个数据的时候
- cnt 则变为了0(表示缓冲区中已经没有任何数据了)
- ptr 变为了0x0042869(示下次应该从这个位置开始从缓冲区中读取数据)
但是此时缓冲区中已经没有任何数据了,所以要将输入流中的剩下的那10个数据放进来,这样缓冲区中又有了10个数据
- 此时 cnt 变为了10
- 当缓冲区中重新放进来数据的时候这个 ptr 的值从0x0042869变为了0x00428e60(当缓冲区中没有任何数据的时候要将 ptr 这个值进行一下刷新,使其指向缓冲区的基地址)
当我们从键盘输入字符串的时候需要敲一下回车键才能够将这个字符串送入到缓冲区中,那么敲入的这个回车键(\r)会被转换为一个换行符\n,这个换行符\n也会被存储在缓冲区中并且被当成一个字符来计算!比如我们在键盘上敲下了123456这个字符串,然后敲一下回车键(\r)将这个字符串送入了缓冲区中,那么此时缓冲区中的字节个数是7 ,而不是6。
【结论】:缓冲区的刷新就是将指针 ptr 变为缓冲区的基地址 ,同时 cnt 的值变为0