专栏首页程序员标准I/O库(ISO C的标准I/O库)

标准I/O库(ISO C的标准I/O库)

本文讲述由ISO C定义的标准I/O库。这个库已经拥有非常长的历史了,它由D.R.在1975年左右编写,现在已经过去45年了。但是ISO C几乎没有对标准I/O库做出修改。不用我说,大家也知道这个库存在的问题应该是非常多的。

标准输入,标准输出,标准出错

Linux下的不带缓冲的I/O是围绕文件描述符来展开的。标准库的则不是,标准库的操作是围绕流(stream)这个概念来进行的。例如:标准输入流,标准输出流,标准出错流。这3个流是自动被进程使用的。他们其实和文件描述符STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO引用相同的文件。

带缓冲的I/O操作

使用文件描述符的I/O是不带缓冲的(当然了,这里所说的不带缓冲指的是进程中使用这两个函数不会自动缓冲,每使用一次就会进行一次系统调用,实际上除了原始磁盘I/O之外,其它的所有磁盘I/O都会经过内核缓冲区。),而标准I/O库为了减少read和write操作,使用了缓冲。

标准I/O提供了缓冲,但是成也萧何,败也萧何啊!这个缓冲的设计也是它的败笔吧!

标准I/O提供了3种缓冲方式。

全缓冲

在这种情况下,在填满标准I/O缓冲区以后,才进行I/O操作。在第一次执行I/O操作的时候,标准I/O会使用malloc来获取所需要的缓冲区。那么这时候有个问题,缓冲区没满就不进行实际的I/O操作,但是你想写入磁盘,怎么办?别慌,标准I/O设计了一个冲洗(flush)操作。你可以使用函数fflush来强制冲洗一个流。冲洗意味着将缓冲区的内容写到磁盘中。

函数fflush()

它有一个特殊情形,就是参数stream是NULL。这个时候表示强制冲洗所有输出流。

行缓冲

行缓冲就是当输入和输出中遇到换行符时,标准I/O执行实际I/O操作。当我们使用scanf和printf的时候,实际上就是行缓冲在起作用。行缓冲的长度是固定的,因此如果你在一行输入的内容过的,导致在你还没有换行的时候,也会发生实际的I/O操作。还有就是当你通过标准I/O库从一个不带缓冲或者是带行缓冲的流得到输入数据。那么就会强制冲洗所有行缓冲的输出流。

不缓冲

标准I/O对字符不进行缓冲。通常标准出错是不带缓冲的,这样就能使的出错信息及时打印出来。

ISO C的规则

  • 当且仅当标准输入和标准输出不指向交互式设备的时候,它们才是全缓冲的。
  • 标准错误一定不会是全缓冲。

规则就是如此的简单粗暴。它只说了什么时候全缓冲和不全缓冲。在Linux下。通常是这样的。

  • 标准错误是不带缓冲的。
  • 标准输入和标准输出,如果指向的设备是终端,那么使用行缓冲,否则使用全缓冲。

更改缓冲方式

我们可以使用下面的库函数来更改缓冲方式。

这些函数的只能在打开流之后调用。所以我们可以看到这些函数的第一个参数都是FILE *。需要注意的是setbuf(),setbuffer()以及setlinebuf其实都将调用setvbuf函数。因此,我们来关注一下setvbuf()函数。

也就是说buf和size是由mode决定的。但是当buf是NULL时,标准I/O会自动为该流分配适当长度的缓冲区(就是size所指定的值)。当然只有这个被指定的模式会受到影响,下次还是会新分配缓冲的。

其余的函数说明如下:

打开流操作

在Linux下这三个函数可以用来打开流。仔细观察可以发现fdopen()函数需要一个文件描述符做参数。而ISO C没有涉及文件描述符,所以只能在POSIX标准之下使用这个函数。另外对于fdopen()而言,它的mode参数的含义也略有不同。这是因为文件的权限在被open或者creat的时候已经指定好了。并且fdopen()函数并不能用来创建一个文件,很明显它需要一个文件描述符,既然有了文件描述符,那么文件肯定已经存在了。好了,下面我们先看一下mode的取值。

值得注意的是Linux内核并不区分文本文件和二进制文件。因此在Linux下使用带有b的参数是没有意义的(没有作用)。

读和写流

输入函数

标准I/O库提供了非常多的函数来进行读写操作。下面给出一些读写相关的函数。

有个问题需要注意,那就是返回值。

fgetc(),getc()和getchar()无论是遇到文件结尾还是错误都会返回同样的值。为了区分这两种情形,必须使用ferror或者feof函数。

clearerr()函数可以用来清楚1.出错标志,2.文件结束标志。上述的fileno函数可以被实现为宏。宏和函数的区别还是比较大的。在使用某些函数的时候,需要注意它是否被实现为宏,如果是,那么意味着一下几点:

1.参数不要具备副作用。

2.不能传递宏的地址,它没有地址。

3.宏比函数快。

输出函数

上述的函数之中,gets()函数由于没有指定缓冲区的大小。这曾造成过1988年的蠕虫事件。因此,当大多数人在Visual Studio2015之后的版本上书写C语言程序的时候,使用gets和scanf函数会报错。

VS不仅报错了,还让你使用scanf_s()函数来代替scanf函数。但是带来问题是比较差的可移植性。在某些类Unix操作系统上已经弃用了该接口。我们尽量不要使用不安全的函数。

二进制I/O

前面的I/O函数都是一次读写一行或者是单个字符,这在读写大文件的时候并不适合。为此,提供了下面的函数来执行二进制I/O操作。

这两个函数仍旧存在一些问题。那就是在不同的系统上工作的时候,可能由于struct对齐方式,以及是否遵从IEEE 754标准造成程序出错。多年之前,所有的Unix操作系统都运行在PDP-11计算机上,所以没有任何问题。

定位流

上述函数在类Unix系统上没有问题,但是如果在Window下可能就行不通。ISO C提供了fgetpos()和fsetpos()函数。

格式化I/O

格式化I/O能够漂亮的处理输入输出,但是格式转换符比较复杂,种类繁多。在此处不说明。只给出相关的函数。

在Unix中,标准I/O库最后还是需要调用不带缓冲的I/O函数。每个标准I/O都有一个与其相关联的文件描述符,可以使用fileno()函数来获得文件描述符。需要注意的是fileno()函数是POSIX标准提供的。

标准I/O的问题以及替代方式

前面已经说过了,标准I/O的历史已经非常长了,它存在问题也比较多。很明显标准I/O的效率不高。它需要在内核缓冲区复制一次数据,然后在用户进程内存中在复制一次数据。

另外的问题可能就是不够安全,微软已经在Windows平台提供了更加安全的函数。

在Linux下替代它们的可以有sfio库,以及使用mmap()函数的ASI包。

前文说过成也萧何,败也萧何。标准I/O使用的缓冲技术正是产生很多问题和混淆的地方。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 模板的一些语法问题

    模板无疑是非常复杂的,一个模板类。你把成员函数实现在类内,是比较简单的。当然,你也可以实现在类外。这时候你有两个选择,在同一个文件实现成员函数,在另一个.cpp...

    zy010101
  • C++之覆盖

    在继承中,C++允许子类的成员和父类同名。此时,子类的同名成员会覆盖父类的同名成员。如果想使用父类的同名成员,需要使用类名+作用域运算符。下面这段代码演示了如何...

    zy010101
  • C++之函数重载

    函数重载是:函数名相同,但是函数参数不同的函数之间的关系。函数重载只能通过函数参数的不同来实现,这包含参数个数不同和参数类型不同。 !!! 重载不...

    zy010101
  • 数据库事务和索引

    事务的性质:   原子性:同一个事务中的所有操作要不然全部成功要不然全部失败   一致性:一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,,也...

    李家酒馆酒保
  • 大数据开发和大数据分析哪个就业发展好?

    第二是对大数据处理系统本身进行开发。对理论和实践要求的都更深一些,也更有技术含量。

    加米谷大数据
  • 未来最赚钱的10大互联网职位,平均月薪最高30k,竟然有地推…你的工作呢?

    Y 总是一个年轻的90后创业者,花两年时间做了一款主打娱乐的App,还有一套 SDK 。

    华章科技
  • Gartner发布未来三年内影响重大的十大信息科技趋势

    市场研究公司Gartner的IT专家预测出了2015年的十大信息科技趋势——如普适计算、物联网、3D打印、丰富环境系统、以及智能机器、云端/客户端架构等——这些...

    机器人网
  • 阿里新零售又一块试验田!揭秘亲橙里的天猫智慧门店

    据悉,亲橙里在该项目中落地“刷脸消费”、“零负担购物”、“AR导购”、“千人千面”魔幻试衣等新零售新技术,也为阿里园区的员工和访客、周边社区居民的吃喝玩乐带来更...

    IT派
  • sass/scss 变量与嵌套规则

    头部定义的变量的作用域是变量以下所有区域, 在{ }内部定义的变量,内部有效,属于局部变量 sass变量不会变量提升,必须先定义后使用。

    祈澈菇凉
  • 小白教程,Springboot项目搭建(前端到数据库,超详细)

    这是一篇入门Springboot的小白教程,但是看到写得这么详细,说不定有同学需要呢?大神看到这里就可以跳过了哈。

    java思维导图

扫码关注云+社区

领取腾讯云代金券