标准 IO 与系统 IO 函数整理(含参数、功能、区别)
本文档基于 Linux 标准 IO / 系统 IO 学习笔记,梳理所有涉及的函数,按标准 IO(文件操作 / 读写 / 定位 / 格式化)和系统 IO分类,明确每个函数的原型、参数、核心功能,并总结同类函数、标准 / 系统 IO 函数的核心区别。
一、标准 IO 函数(<stdio.h>)
标准 IO 是 C 标准库实现的文件操作接口,封装缓冲区,遵循 POSIX 标准,可跨平台使用,核心围绕FILE*文件流指针操作,Linux 下默认对普通文件全缓冲、标准输出行缓冲、标准错误无缓冲。
(一)文件基础操作(打开 / 关闭 / 刷新)
函数原型核心参数说明功能返回值关键备注
| FILE *fopen(const char *pathname, const char *mode); |
pathname:文件路径 / 名;mode:打开模式(r/r+/w/w+/a/a+/rb/wb) |
以指定模式打开文件,关联文件流 |
成功:FILE*文件流指针;失败:NULL |
打开失败会设置errno,可通过perror打印;rb/wb为二进制模式,避免 Windows 换行符转换 |
| int fclose(FILE *stream); |
stream:已打开的文件流指针 |
关闭文件流,刷新缓冲区,释放堆内存(FILE 结构体) |
成功:0;失败:EOF(-1) |
不可重复关闭,重复关闭会导致段错误(堆内存重复释放);关闭时自动刷新未写入的缓冲数据 |
| int fflush(FILE *stream); |
stream:文件流指针(NULL 表示刷新所有输出流) |
手动刷新缓冲区,将缓冲数据写入实际文件 |
成功:0;失败:EOF(-1) |
仅对输出流 / 更新流有效;标准输出行缓冲下,\\n/ 程序结束 /fflush 都会触发刷新 |
(二)字符级读写(单字符操作)
函数原型核心参数说明功能返回值关键备注
| int fgetc(FILE *stream); |
stream:待读取的文件流 |
从文件流读取一个字符,文件光标后移 1 字节 |
成功:字符 ASCII 码(unsigned char 转 int);失败 / 到末尾:EOF(-1) |
可通过feof/ferror区分末尾和错误 |
| int getc(FILE *stream); |
同 fgetc |
功能与 fgetc 完全一致 |
同 fgetc |
实现为宏定义,可能多次计算stream,避免传入带副作用的表达式 |
| int getchar(void); |
无参数 |
从标准输入 stdin读取一个字符 |
同 fgetc |
等价于getc(stdin) |
| int fputc(int c, FILE *stream); |
c:待写入字符的 ASCII 码;stream:目标文件流 |
向文件流写入一个字符,光标后移 1 字节 |
成功:写入的字符 ASCII 码;失败:EOF(-1) |
追加模式(a/a+)下,始终写入文件末尾 |
| int putc(int c, FILE *stream); |
同 fputc |
功能与 fputc 完全一致 |
同 fputc |
实现为宏定义 |
| int putchar(int c); |
c:待写入字符的 ASCII 码 |
向标准输出 stdout写入一个字符 |
同 fputc |
等价于putc(c, stdout) |
(三)行级读写(按行操作)
函数原型核心参数说明功能返回值关键备注
| char *fgets(char *s, int n, FILE *stream); |
s:存储数据的缓冲区;n:缓冲区大小;stream:待读取文件流 |
从文件流读取一行数据,最多读n-1个字符(留 1 位存\\0),遇\\n/EOF/ 满缓冲区停止,保留\\n |
成功:缓冲区指针s;失败 / 到末尾且无数据:NULL |
可通过feof/ferror区分末尾和错误;解决了gets的缓冲区溢出问题 |
| int fputs(const char *s, FILE *stream); |
s:待写入的字符串;stream:目标文件流 |
向文件流写入字符串,不写入末尾的\\0 |
成功:非负整数;失败:EOF(-1) |
不会自动添加\\n,需手动在字符串中加入 |
| int puts(const char *s); |
s:待写入的字符串 |
向标准输出 stdout写入字符串,自动添加\\n |
成功:非负整数;失败:EOF(-1) |
等价于fputs(s, stdout) + putchar('\\n') |
(四)块级读写(按数据块操作,高效)
函数原型核心参数说明功能返回值关键备注
| size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); |
ptr:存储数据的缓冲区;size:单个数据块的字节数;nmemb:要读取的块数;stream:待读取文件流 |
从文件流读取nmemb 个 size 字节的块,总读取字节数 = size×nmemb |
成功:实际读取的块数;失败 / 到末尾:小于 nmemb 或 0 |
二进制模式下使用更高效;返回值 < nmemb 时,需feof(stream)判断是否到末尾,ferror(stream)判断是否出错 |
| size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); |
ptr:待写入数据的缓冲区;其余同 fread |
向文件流写入nmemb 个 size 字节的块,总写入字节数 = size×nmemb |
成功:实际写入的块数;失败:小于 nmemb 或 0 |
二进制模式下可拷贝任意文件(文本 / 图片 / 视频);写入失败仅因 IO 错误,无 “到末尾” 情况 |
(五)文件定位(光标 / 偏移量操作)
函数原型核心参数说明功能返回值关键备注
| int fseek(FILE *stream, long offset, int whence); |
stream:文件流;offset:偏移量(可正可负);whence:基准点(SEEK_SET / 开头、SEEK_CUR / 当前、SEEK_END / 末尾) |
设置文件流的光标偏移量 |
成功:0;失败:非 0 |
二进制文件:offset 可任意设置;文本文件:offset 只能为 0 或 ftell 返回值,且 whence 为 SEEK_SET |
| long int ftell(FILE *stream); |
stream:文件流 |
获取当前光标相对于文件开头的偏移量(字节数) |
成功:偏移量(长整型);失败:-1L |
结合fseek(fp, 0, SEEK_END)可快速获取文件大小 |
| void rewind(FILE *stream); |
stream:文件流 |
将光标重置到文件开头,同时清除文件的错误 / 结束标志 |
无返回值 |
等价于fseek(stream, 0L, SEEK_SET) + clearerr(stream) |
(六)错误 / 结束判断
函数原型核心参数说明功能返回值关键备注
| int feof(FILE *stream); |
stream:文件流 |
判断文件流是否到达末尾(EOF) |
是:非 0 值;否:0 |
仅在读操作后判断才有意义,未读操作时默认返回 0 |
| int ferror(FILE *stream); |
stream:文件流 |
判断文件流是否发生读写错误 |
是:非 0 值;否:0 |
错误标志需通过clearerr清除 |
| void clearerr(FILE *stream); |
stream:文件流 |
清除文件流的结束标志(feof)和错误标志(ferror) |
无返回值 |
重置后可重新判断文件状态 |
(七)格式化读写(按指定格式输入输出)
函数原型核心参数说明功能返回值关键备注
| int fprintf(FILE *stream, const char *format, …); |
stream:目标文件流;format:格式化字符串;…:可变参数 |
向指定文件流写入格式化数据 |
成功:写入的字符数;失败:EOF(-1) |
标准输出版为printf,等价于fprintf(stdout, format, …) |
| int fscanf(FILE *stream, const char *format, …); |
stream:待读取文件流;其余同 fprintf |
从指定文件流读取格式化数据到变量 |
成功:成功匹配的参数个数;失败 / 到末尾:EOF(-1) |
标准输入版为scanf,等价于fscanf(stdin, format, …);会自动跳过空白符(空格 / 换行 / Tab) |
| int sprintf(char *s, const char *format, …); |
s:存储格式化数据的缓冲区;其余同 fprintf |
向内存缓冲区写入格式化数据,自动添加\\0 |
成功:写入的字符数(不含\\0);失败:负数 |
存在缓冲区溢出风险,推荐使用snprintf |
| int snprintf(char *s, size_t n, const char *format, …); |
n:缓冲区最大字节数;其余同 sprintf |
向缓冲区写入格式化数据,最多写 n-1 个字符,自动加\\0 |
成功:预期写入的字符数;失败:负数 |
安全版 sprintf,避免缓冲区溢出;若返回值 >=n,说明数据被截断 |
二、系统 IO 函数(Linux 内核提供,无缓冲区)
系统 IO 是 Linux 内核直接提供的文件操作接口,无封装缓冲区,基于 ** 文件描述符(fd,非负整数)** 操作,仅适用于 Linux/Unix 系统,可操作所有类型文件(普通文件 / 设备 / 套接字 / 链接),实时性高,适合硬件设备交互。
(一)文件基础操作(打开 / 关闭)
| 函数原型 |
核心参数说明 |
功能 |
返回值 |
关键备注 |
| int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); |
pathname:文件路径 / 名;flags:打开标志(O_RDONLY/O_WRONLY/O_RDWR/O_CREAT/O_TRUNC/O_APPEND);mode:文件权限(八进制,如 0644,仅 O_CREAT 时有效) |
打开 / 创建文件,分配文件描述符 |
成功:未使用的最小非负整数(fd);失败:-1 |
|
| int close(int fd); |
fd:文件描述符 |
关闭文件描述符,释放内核文件资源 |
文件资源成功:0;失败:-1 |
|
(二)块级读写(仅支持数据块操作)
函数原型核心参数说明功能返回值关键备注
| ssize_t read(int fd, void *buf, size_t count); |
fd:文件描述符;buf:存储数据的缓冲区;count:期望读取的最大字节数 |
从 fd 指向的文件读取最多 count 字节到缓冲区 |
成功:实际读取的字节数;失败:-1;到末尾:0 |
无缓冲区,每次调用都是系统调用;读取普通文件时,返回值 < count 即到末尾 |
| ssize_t write(int fd, const void *buf, size_t count); |
fd:文件描述符;buf:待写入数据的缓冲区;count:期望写入的字节数 |
向 fd 指向的文件写入最多 count 字节从缓冲区 |
成功:实际写入的字节数;失败:-1 |
追加模式(O_APPEND)下,写入前自动将偏移量移到文件末尾;写入磁盘时可能因磁盘满导致返回值 < count |
(三)文件定位(偏移量操作)
函数原型核心参数说明功能返回值关键备注
| off_t lseek(int fd, off_t offset, int whence); |
fd:文件描述符;offset:偏移量(可正可负);whence:基准点(SEEK_SET/SEEK_CUR/SEEK_END,同 fseek) |
设置文件描述符的偏移量 |
成功:新偏移量(相对于文件开头的字节数);失败:(off_t)-1 |
可通过lseek(fd, 0, SEEK_END)快速获取文件大小;支持将偏移量设为文件末尾之后(形成 “文件空洞”) |
(四)时间函数(辅助作业,<time.h>)
函数原型核心参数说明功能返回值关键备注
| time_t time(time_t *tloc); |
tloc:存储时间的指针(一般传 NULL) |
获取从1970-01-01 00:00:00 UTC到当前的秒数(时间戳) |
成功:时间戳(time_t 为长整型);失败:(time_t)-1 |
需结合localtime/strftime转换为本地时间和指定格式 |
三、核心区别总结
(一)同类标准 IO 函数的区别
- fgetc/getc/getchar:功能一致,fgetc 是函数,getc 是宏,getchar 是针对 stdin 的封装;
- fputc/putc/putchar:功能一致,fputc 是函数,putc 是宏,putchar 是针对 stdout 的封装;
- fgets/gets:fgets 安全(指定缓冲区大小),保留\\n;gets 存在缓冲区溢出,已被废弃;
- fputs/puts:fputs 不自动加\\n,可指定文件流;puts 自动加\\n,仅输出到 stdout;
- sprintf/snprintf:snprintf 是安全版,指定缓冲区大小,避免溢出;sprintf 无大小限制,易溢出;
- printf/fprintf:printf 是 fprintf 针对 stdout 的封装,fprintf 可指定任意文件流。
(二)标准 IO 与系统 IO 的核心区别
对比维度标准 IO(C 库)系统 IO(Linux 内核)
| 操作句柄 |
FILE*文件流指针(结构体,封装 fd 和缓冲区) |
文件描述符fd(非负整数,内核 fd_array 数组下标) |
| 缓冲区 |
封装用户态缓冲区(全缓冲 / 行缓冲 / 无缓冲),减少系统调用 |
无用户态缓冲区,每次操作都是直接系统调用 |
| 跨平台性 |
遵循 POSIX/ANSI C 标准,可在 Linux/Windows 等跨平台使用 |
仅适用于 Linux/Unix 类系统,与内核强绑定 |
| 支持文件类型 |
仅适用于普通文件 / 标准输入输出 |
支持所有 Linux 文件类型(普通 / 设备 / 套接字 / 链接 / 管道) |
| 效率 |
普通文件操作效率高(缓冲区减少系统调用) |
普通文件操作效率低(频繁系统调用),但硬件设备实时性高 |
| 内存管理 |
fopen 申请堆内存(FILE 结构体),fclose 释放,重复关闭段错误 |
open/close 无堆内存操作,仅操作内核资源,重复关闭无错误 |
| 函数丰富度 |
函数丰富(字符 / 行 / 块 / 格式化读写,定位 / 刷新 / 错误判断) |
函数简洁(仅块读写 / 定位 / 打开关闭),无格式化 / 字符 / 行操作 |
| 头文件 |
核心 <**stdio.h**>,时间函数 <time.h> |
核心 <**unistd.h**>、<sys/types.h>、<fcntl.h> |
(三)fread/fwrite 与 read/write 的关键区别
- 返回值:fread/fwrite 返回实际操作的块数,read/write 返回实际操作的字节数;
- 错误判断:fread/fwrite 返回值 < 指定值时,需feof/ferror区分末尾 / 错误;read/write 返回 – 1 为错误,0 为末尾;
- 缓冲区:fread/fwrite 基于用户态缓冲区,底层调用 read/write;read/write 无缓冲区,直接操作内核;
- 跨平台:fread/fwrite 可跨平台,read/write 仅 Linux/Unix 可用。
(四)fseek/ftell 与 lseek 的关键区别
- 操作句柄:fseek/ftell 操作FILE*,lseek 操作fd;
- 参数 / 返回值类型:fseek 的 offset 是long,ftell 返回long int;lseek 的 offset 是off_t,返回off_t(64 位系统支持更大偏移);
- 文本文件限制:fseek 对文本文件偏移量有限制(仅 0/ftell 返回值);lseek 对文本 / 二进制文件无限制;
- 文件空洞:lseek 支持将偏移量设为文件末尾之后,形成 “文件空洞”;fseek 不推荐此操作。
评论前必须登录!
注册