对C语言标准IO的理解

对标准IO的理解:

个人理解,如果有错误请指出

  • c语言标准IO通过一个FILE结构来封装了readwrite系统调用,并且标准IO缓冲区的分配也是通过修改FILE的元素来改变,
  • 由于常用stdin,stdout,就认为缓冲区是分为输入缓冲区和输出缓冲区的,这只是因为缓冲区承担一项工作(输入or输出)

如果用文件流以O_RDWR状态打开一个文件,那么缓冲区将会同时作为输入和输出的缓冲区,

看一段代码cppreference

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* fflush example */
#include <stdio.h>
char mybuffer[80];
int main()
{
FILE * pFile;
pFile = fopen ("example.txt","r+");
if (pFile == NULL) perror ("Error opening file");
else {
fputs ("test",pFile);
//fflush (pFile); // flushing or repositioning required
fgets (mybuffer,80,pFile);
puts (mybuffer);
fclose (pFile);
return 0;
}
}
  1. 取消缓冲区刷新,那么缓冲区的数据不会写入pFile指向的文件,注释掉fflush(pFile),然后编译运行查看结果.

    发现待写入pFile的数据仍在缓冲区中,且这一部分占据了从流中读入数据的对应部分,

    fclose函数在文件被关闭之前,冲洗缓冲区的输出数据,缓冲区中的任何输入数据被丢弃。

    于是留在缓冲区中待写入pFile的数据最终被写入文件,

  2. 开启缓冲区刷新(在输出之后输入之前刷新缓冲区),编译运行,

    写入的数据写入后,读入缓冲区的数据仍被占用???,

    原因(推测):

    fputs执行完之后fflush执行之前.此时缓冲区(也称为流)的读写位置是fputs写入缓冲区的长度,那么fgets读入的数据只能从这个地方开始,

    • 如何更改缓冲区(流)的读写位置?且刷新缓冲区

      可以通过重新定位(fseekfsetposrewind)完成此操作,

对比文件流和内存流来理解标准IO

内存流(把内存当做文件用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include "apue.h"

static char buf[] = "ttest file";

int main(int argc,char *argv[]) {
int len = strlen(buf);
// 打开流的模式,即打开文件的模式
FILE *fp = fmemopen(buf,len,"r+");
if (fp == NULL) {
printf("get file error\n");
exit(1);
}

char ch;
// 把内存当做文件用,从内存(文件)中读入字符到标准IO的缓冲区,然后提取出来
while ((ch = fgetc(fp)) != EOF) {
printf("%c",ch);
}

return 0;
}
  • 通过下面来理解内存流和文件流的差别,然后理解标准IO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BSZ 48
char buf[BSZ];

int main(int argc,char *argv[]) {
FILE *fp;

// 1
memset(buf,'a',BSZ - 2);
buf[BSZ - 2] = '\0';
buf[BSZ - 1] = 'X';
// 打开文件流
if ((fp = fmemopen(buf,BSZ,"w+")) == NULL) {
printf("fmemopen error");
exit(1);
}
// stdout打印缓冲区
printf("initial buffer content: %s\n",buf);
// 打开水龙头,向文件(内存)中写入,但此时只是停留在标准IO的缓冲区中,
fprintf(fp,"hello,world");
printf("before standard buffer flush: %s\n",buf);
// fflush强制IO缓冲区的数据写入fp指定的文件中,fmemopen保证了文件是内存
fflush(fp);
printf("after fflush: %s\n",buf);
printf("len of string in buf = %ld\n",(long)strlen(buf));

//2
memset(buf,'b',BSZ - 2);
buf[BSZ - 2] = '\0';
buf[BSZ - 1] = 'X';
// 向fp指向的文件写入数据,但是还停留在IO库的缓冲区中
fprintf(fp,"hello, world");
// reposition会导致缓冲区被flush
fseek(fp,0,SEEK_SET);
// 此时buf应该都为 b
printf("after fseek: %s\n",buf);
printf("len of string in buf = %ld\n",(long)strlen(buf));

memset(buf,'c',BSZ - 2);
buf[BSZ - 2] = '\0';
buf[BSZ - 1] = 'X';
// 流的读写位置通过seek定位到了0,fclose之前会flush缓冲区,
fprintf(fp,"hello,world");
// 关闭流之前,io库自动刷新所有的IO库的缓冲区
fclose(fp);
printf("after fclose: %s\n",buf);
printf("len of string in buf = %ld\n",(long)strlen(buf));

return 0;
}

流和文件相关联,那么每个进程默认的3个std流引用的是哪3个文件?

  • 引用的是STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO这3个文件描述符所引用的文件

流其实就是文件,标准IO库在操作系统的IO例程上封装.FILE*指向的结构用来描述流的信息:缓冲区长度,缓冲区地址,打开的文件描述符等?

  • 一般用于将一个指定的文件打开为一个预定的流(文件->流),

把流看做水龙头的开关更好理解,普通流则水来自磁盘文件,内存流则水来自内存

  • ISO C 2011 标准中gets弃用,那么最好也不要使用puts

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!