close: 用于关闭文件描述符,是操作系统级别的函数。fclose: 用于关闭文件指针,是C标准库级别的函数。
下面我们通过一个详细的对比表格和解释来彻底搞懂它们。

(图片来源网络,侵删)
核心概念:文件描述符 vs. 文件指针
要理解 close 和 fclose 的区别,首先必须理解它们操作的对象。
| 特性 | 文件描述符 | 文件指针 |
|---|---|---|
| 层级 | 操作系统 层级 | C标准库 层级 |
| 本质 | 一个非负整数,是内核为了管理一个被打开的文件(或设备、套接字等)而创建的索引。 | 一个指向 FILE 结构体(在 stdio.h 中定义)的指针。 |
| 作用域 | 进程级别的,每个进程都有自己独立的文件描述符表。 | C标准库缓冲区的管理。 |
| 工作方式 | 直接与内核交互,进行I/O操作(读、写、等)。 | 在用户空间维护一个缓冲区,减少与内核的交互次数,提高效率。 |
| 如何获取 | 通过 open(), socket(), pipe() 等系统调用返回。 |
通过 fopen() 函数返回。 |
| 如何关闭 | close() |
fclose() |
close() 函数详解
close() 是一个系统调用,直接与操作系统内核交互。
函数原型
#include <unistd.h> // 在 Linux/Unix 系统中 int close(int fd);
fd: 要关闭的文件描述符。
功能
- 释放与文件描述符
fd相关的系统资源。 - 将
fd从当前进程的文件描述符表中移除,这个整数标识符可以被后续的open()等系统调用复用。 - 当进程正常终止时,操作系统会自动关闭该进程打开的所有文件描述符,但显式调用
close()是一个好习惯,可以立即释放资源。
返回值
- 成功:返回
0。 - 失败:返回
-1,并设置errno来表示具体的错误。
使用场景
- 主要用于 Linux/Unix 环境下的底层文件操作。
- 网络编程中关闭套接字 (
socket)。 - 操作管道、设备文件等。
示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
int main() {
const char *filepath = "test.txt";
int fd;
// 使用 open 系统调用创建/打开文件,返回文件描述符
fd = open(filepath, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("open failed");
exit(EXIT_FAILURE);
}
printf("File opened successfully with fd: %d\n", fd);
// 写入一些数据
write(fd, "Hello, OS level!", 16);
// 使用 close 系统调用关闭文件
if (close(fd) == -1) {
perror("close failed");
exit(EXIT_FAILURE);
}
printf("File with fd %d closed.\n", fd);
return 0;
}
fclose() 函数详解
fclose() 是 C 标准库中的一个函数,它位于更高抽象层次。
函数原型
#include <stdio.h> int fclose(FILE *stream);
stream: 指向要关闭的FILE对象(即文件指针)的指针。
功能
- 刷新缓冲区:这是
fclose()最关键的功能之一,在调用fclose()时,它会将FILE结构体内部缓冲区中所有尚未写入磁盘的数据强制写入,然后再关闭文件,这可以防止数据丢失。 - 释放资源:它会调用
close()系统调用来关闭底层的文件描述符。 - 释放
FILE结构体:它会释放 C 标准库为该文件流分配的内存(即FILE结构体本身)。
返回值
- 成功:返回
0。 - 失败:返回
EOF(通常是-1),并设置errno。
使用场景
- 绝大多数在 C 语言中进行文件操作的场景(在 Windows、Linux、macOS 上进行可移植的程序开发)。
- 当你使用
fopen(),fprintf(),fscanf(),fread(),fwrite()等标准库函数时,你必须使用fclose()来对应。
示例
#include <stdio.h>
int main() {
const char *filepath = "test_stdio.txt";
FILE *fp;
// 使用 fopen 标准库函数打开文件,返回文件指针
fp = fopen(filepath, "w");
if (fp == NULL) {
perror("fopen failed");
return EXIT_FAILURE;
}
printf("File opened successfully with FILE pointer: %p\n", (void*)fp);
// 使用 fprintf 写入数据,数据可能还在缓冲区中
fprintf(fp, "Hello, C standard library!");
// 使用 fclose 关闭文件
// 缓冲区的数据会被写入磁盘,fclose 内部会调用 close()
if (fclose(fp) == EOF) {
perror("fclose failed");
return EXIT_FAILURE;
}
printf("File with FILE pointer %p closed.\n", (void*)fp);
return 0;
}
关系与区别总结
| 特性 | close() |
fclose() |
|---|---|---|
| 所属库 | 操作系统系统调用 (POSIX) | C标准库 (stdio.h) |
| 操作对象 | 文件描述符 (int) |
文件指针 (FILE*) |
| 主要目的 | 告诉内核不再使用该文件描述符 | 清理C标准库的缓冲区,并最终调用 close() |
| 缓冲区 | 没有用户空间缓冲区,直接进行I/O。 | 有用户空间缓冲区。fclose() 会刷新缓冲区。 |
| 使用方式 | fd = open(...); ...; close(fd); |
FILE *fp = fopen(...); ...; fclose(fp); |
| 抽象层级 | 低层,直接与硬件/内核交互 | 高层,更方便、安全,有缓冲机制 |
| 性能 | 频繁I/O,性能较低 | 批量I/O,性能较高(因为减少了系统调用次数) |
一个绝佳的比喻:银行柜台 vs. ATM
-
fopen()和fclose()(C标准库):
(图片来源网络,侵删)- 你去银行柜台办理业务,银行柜员(
FILE结构体)会给你一个临时的小本子(缓冲区)。 - 你告诉柜员要存钱、取钱(
fprintf,fscanf),柜员先记在小本子上,不会立刻去金库。 - 当你办完所有业务,对柜员说“结束了”(
fclose()),柜员会把小本子上所有的记录一次性处理掉(刷新缓冲区),然后去金库完成真正的操作(调用close()),最后送走你。
- 你去银行柜台办理业务,银行柜员(
-
open()和close()(系统调用):- 你直接去金库门口,每次你要存或取钱(
read,write),都必须向守卫(操作系统内核)申请开门,完成一笔交易,然后关门,这个过程非常直接,但每次都要和守卫交互,效率较低。
- 你直接去金库门口,每次你要存或取钱(
何时使用哪个?
- 总是优先使用
fopen()/fclose():除非你有特殊需求(如需要精细控制文件权限、进行非标准I/O、或者进行网络编程),否则在C语言中处理文件,都应该使用标准库的fopen/fclose系列,它更安全、更高效(因为缓冲)、可移植性更好。 - 使用
open()/close():当你需要进行底层的、非缓冲的I/O操作时,- 网络编程中的
socket。 - 设备驱动编程。
- 需要使用
open()的特殊标志(如O_NONBLOCK非阻塞模式)。 - 在性能极端敏感的场景下,手动管理I/O(但这通常不推荐,因为标准库的缓冲机制已经非常优化)。
- 网络编程中的

(图片来源网络,侵删)
