对C语言标准IO的理解

微笑、不失礼 提交于 2020-01-31 04:55:47

对标准IO的理解:

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

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

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

看一段代码cppreference

/* 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

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

#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
#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
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!