📌 一句话总结
open() / read() / write() / close() |
fopen() / fread() / fwrite() / fclose() |
|
---|---|---|
所属层级 | 系统调用(System Call) ------ 内核接口 | 标准 C 库函数(Standard I/O) ------ 用户库封装 |
头文件 | <fcntl.h> , <unistd.h> |
<stdio.h> |
返回类型 | int (文件描述符,file descriptor) |
FILE* (文件流指针) |
缓冲机制 | 无缓冲(每次调用都陷入内核) | 带缓冲(减少系统调用次数,提高效率) |
可移植性 | 类 Unix 系统(Linux/macOS/WSL),非 ANSI C | ANSI C 标准,跨平台(Windows/Linux/macOS) |
适用场景 | 高性能、底层控制、设备文件、mmap 等 |
一般文件读写、文本处理、快速开发 |
🔍 详细对比
1. 层级与实现
-
open()
系列:- 是 操作系统提供的系统调用,直接与内核交互。
- 每次
read()
/write()
都会触发一次 系统调用(syscall) ,开销较大。
-
fopen()
系列:- 是 C 标准库(如 glibc)对系统调用的封装。
- 内部使用缓冲区(通常 4KB~8KB),只有缓冲区满或显式刷新(
fflush
)时才调用write()
。
✅ 举例:
用
fwrite()
写 1000 次 1 字节 → 可能只触发 1 次write()
系统调用。用
write()
写 1000 次 1 字节 → 触发 1000 次 系统调用(慢!)。
2. 缓冲机制(关键区别!)
函数系列 | 缓冲类型 | 行为 |
---|---|---|
fopen |
全缓冲(文件) / 行缓冲(终端) | 数据先存入 FILE* 的内部缓冲区 |
open |
无缓冲 | 数据直接传给内核 |
scss
// 标准 I/O:可能看不到立即输出
FILE *fp = fopen("log.txt", "w");
fprintf(fp, "Hello"); // 数据在缓冲区,未写入磁盘!
// 需要 fclose(fp) 或 fflush(fp) 才真正写入
// 系统调用:立即写入
int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);
write(fd, "Hello", 5); // 立即进入内核,写入磁盘(或 page cache)
3. 返回值与错误处理
返回值 | 错误检查方式 | |
---|---|---|
open |
fd (≥0 成功,-1 失败) |
检查返回值是否为 -1,用 errno 或 perror() |
fopen |
FILE* (非 NULL 成功,NULL 失败) |
检查是否为 NULL,用 perror() |
4. 功能与灵活性
能力 | open() 系列 |
fopen() 系列 |
---|---|---|
打开设备文件(如 /dev/tty ) |
✅ 支持 | ⚠️ 可能支持,但不推荐 |
非阻塞 I/O、异步 I/O | ✅ 支持(配合 O_NONBLOCK 等) |
❌ 不支持 |
与 mmap() 配合使用 |
✅ 必须用 open() (mmap 需要 fd ) |
❌ 不能直接用(需 fileno() 转换,不推荐) |
文本模式自动换行转换(Windows) | ❌ 无 | ✅ "r" /"w" 模式自动处理 \n ↔ \r\n |
格式化输入输出(printf /scanf ) |
❌ 无 | ✅ 支持 fprintf , fscanf |
5. 性能对比(典型场景)
场景 | 推荐使用 |
---|---|
读写大文件(如 1GB 视频) | 两者均可,open + 大块 read 更可控 |
频繁小量写入(如日志) | ✅ fopen (缓冲减少 syscall) |
需要 mmap 、fcntl 、ioctl |
✅ 必须用 open |
跨平台程序(Windows + Linux) | ✅ fopen (标准 C) |
实时性要求高、避免缓冲延迟 | ✅ open (或 setvbuf 关闭缓冲) |
🔄 如何互相转换?
虽然不推荐混用,但技术上可行:
ini
#include <stdio.h>
#include <unistd.h>
FILE *fp = fopen("test.txt", "w");
int fd = fileno(fp); // FILE* → fd
int fd2 = open("test2.txt", O_WRONLY | O_CREAT, 0666);
FILE *fp2 = fdopen(fd2, "w"); // fd → FILE*
⚠️ 注意:混用可能导致缓冲不一致(如先
write(fd)
再fwrite(fp)
),强烈建议不要混用同一个文件!
✅ 总结:如何选择?
你想要...... | 用哪个? |
---|---|
快速读写文本文件、做作业、写小程序 | fopen 系列(简单、安全、标准) |
使用 mmap() 、控制文件锁、操作设备 |
open 系列(底层、灵活) |
最高性能(自己管理缓冲) | open + 大块 read /write |
跨平台兼容性 | fopen |
确保数据立即写入磁盘 | open + fsync() ,或 fopen + fflush() + fsync(fileno(fp)) |
💡 记住这个口诀:
"标准 I/O 用
fopen
,底层控制用open
;
要mmap
必open
,跨平台选fopen
。"