1 标准 I/O 库简介
所谓标准 I/O 库则是标准
C
库中用于文件
I/O
操作(譬如读文件、写文件等)相关的一系列库函数的集合,通常标准 I/O
库函数相关的函数定义都在头文件
<stdio.h>
中,所以我们需要在程序源码中包含
<stdio.h>
头文件。标准 I/O
库函数是构建于文件
I/O(
open()
、
read()
、
write()
、
lseek()
、
close()
等)这些系统调用之上的,譬如标准 I/O
库函数
fopen()
就利用系统调用
open()
来执行打开文件的操作、
fread()
利用系统调用
read()
来执行读文件操作、fwrite()
则利用系统调用
write()
来执行写文件操作等等。那既然如此,为何还需要设计标准 I/O
库?直接使用文件
I/O
系统调用不是更好吗?事实上,并非如此,设计库函数是为了提供比底层系统调用更为方便、好用的调用接口,虽然标准 I/O
构建于文件
I/O
之上,但标准
I/O
却有它自己的优势,标准
I/O
和文件
I/O
的区别如下:
⚫ 虽然标准 I/O
和文件
I/O
都是
C
语言函数,但是标准
I/O
是标准
C
库函数,而文件
I/O
则是
Linux系统调用;
⚫ 标准 I/O
是由文件
I/O
封装而来,标准
I/O
内部实际上是调用文件
I/O
来完成实际操作的;
⚫ 可移植性:标准 I/O
相比于文件
I/O
具有更好的可移植性,通常对于不同的操作系统,其内核向应用层提供的系统调用往往都是不同,譬如系统调用的定义、功能、参数列表、返回值等往往都是不一样的;而对于标准 I/O
来说,由于很多操作系统都实现了标准
I/O
库,标准
I/O
库在不同的操作系统之间其接口定义几乎是一样的,所以标准 I/O
在不同操作系统之间相比于文件
I/O
具有更好的可移植性。
⚫ 性能、效率:标准 I/O
库在用户空间维护了自己的
stdio
缓冲区,所以标准
I/O
是带有缓存的,而文件 I/O
在用户空间是不带有缓存的,所以在性能、效率上,标准
I/O
要优于文件
I/O
。
2 FILE 指针
所有文件 I/O
函数(
open()
、
read()
、
write()
、
lseek()
等)都是围绕文件描述符进行的,当调用 open()
函数打开一个文件时,即返回一个文件描述符
fd
,然后该文件描述符就用于后续的
I/O
操作。而对于标准 I/O
库函数来说,它们的操作是围绕
FILE
指针进行的,当使用标准
I/O
库函数打开或创建一个文件时,会返回一个指向 FILE
类型对象的指针(
FILE *
),使用该
FILE
指针与被打开或创建的文件相关联,然后该 FILE
指针就用于后续的标准
I/O
操作(使用标准
I/O
库函数进行
I/O
操作),所以由此可知,FILE 指针的作用相当于文件描述符,只不过
FILE
指针用于标准
I/O
库函数中、而文件描述符则用于文件I/O 系统调用中。
FILE 是一个结构体数据类型,它包含了标准 I/O
库函数为管理文件所需要的所有信息,包括用于实际I/O 的文件描述符、指向文件缓冲区的指针、缓冲区的长度、当前缓冲区中的字节数以及出错标志等。
FILE数据结构定义在标准 I/O
库函数头文件
stdio.h
中。
3
标准输入、标准输出和标准错误
所谓标准输入设备指的就是计算机系统的标准的输入设备,通常指的是计算机所连接的键盘;而标准输出设备指的是计算机系统中用于输出标准信息的设备,通常指的是计算机所连接的显示器;标准错误设备则指的是计算机系统中用于显示错误信息的设备,通常也指的是显示器设备。
用户通过标准输入设备与系统进行交互,进程将从标准输入(stdin
)文件中得到输入数据,将正常输出数据(譬如程序中 printf
打印输出的字符串)输出到标准输出(
stdout
)文件,而将错误信息(譬如函数调用报错打印的信息)输出到标准错误(stderr
)文件。
标准输出文件和标准错误文件都对应终端的屏幕,而标准输入文件则对应于键盘。
每个进程启动之后都会默认打开标准输入、标准输出以及标准错误,得到三个文件描述符,即 0、1、 2,其中 0 代表标准输入、1 代表标准输出、2 代表标准错误;在应用编程中可以使用宏 STDIN_FILENO、STDOUT_FILENO 和 STDERR_FILENO 分别代表 0、1、2,这些宏定义在 unistd.h 头文件中:
/* Standard file descriptors. */
#define STDIN_FILENO 0 /* Standard input. */
#define STDOUT_FILENO1 /* Standard output. */
#define STDERR_FILENO2 /* Standard error output. */
0、
1
、
2
这三个是文件描述符,只能用于文件
I/O
(
read()
、
write()
等),那么在标准
I/O
中,自然是无法使用文件描述符来对文件进行 I/O
操作的,它们需要围绕
FILE
类型指针来进行,在
stdio.h
头文件中有相应的定义,如下:
/* Standard streams. */
extern struct _IO_FILE *stdin; /* Standard input stream. */
extern struct _IO_FILE *stdout; /* Standard output stream. */
extern struct _IO_FILE *stderr; /* Standard error output stream. */
/* C89/C99 say they're macros. Make them happy. */
#define stdin stdin
#define stdout stdout
#define stderr stderr
struct _IO_FILE 结构体就是 FILE 结构体,使用了 typedef 进行了重命名。所以,在标准 I/O
中,可以使用
stdin
、
stdout
、
stderr
来表示标准输入、标准输出和标准错误。