【发布时间】:2011-01-26 06:55:11
【问题描述】:
我想知道文件描述符和文件指针的区别。
另外,在什么情况下您会使用其中一个而不是另一个?
【问题讨论】:
标签: c file-descriptor file-pointer
我想知道文件描述符和文件指针的区别。
另外,在什么情况下您会使用其中一个而不是另一个?
【问题讨论】:
标签: c file-descriptor file-pointer
文件描述符是一个低级整数“句柄”,用于在 Linux 和其他类 Unix 系统中识别内核级别打开的文件(或套接字,或其他)。
您将“裸”文件描述符传递给实际的 Unix 调用,例如 @987654321@、@987654322@ 等。
FILE 指针是 C 标准库级别的构造,用于表示文件。 FILE 包装了文件描述符,并添加了缓冲和其他功能以使 I/O 更容易。
您将FILE 指针传递给标准C 函数,例如@987654323@ 和@987654324@。
【讨论】:
fd 是read() 的第一个参数。为什么叫裸体?
FILE * 类型相比,整数文件描述符是“较少包装”的,即“裸”。
一个被缓冲(FILE *),另一个没有。在实践中,您几乎总是希望在从“真实”文件(即驱动器上)读取时使用FILE *,除非您知道自己在做什么或者除非您的文件实际上是一个套接字左右..
你可以使用fileno()从FILE *获取文件描述符,你可以使用fdopen()从文件描述符打开一个缓冲的FILE *
【讨论】:
文件描述符只是一个整数,您可以从 POSIX open() 调用中获得。使用标准 C fopen() 你会得到一个 FILE 结构体。 FILE 结构包含此文件描述符以及其他内容,例如文件结尾和错误指示符、流位置等。
因此,与open() 相比,使用fopen() 可以为您提供一定程度的抽象。一般来说,您应该使用fopen(),因为它更便于移植,并且您可以使用使用FILE 结构的所有其他标准C 函数,即fprintf() 和家族。
两者都没有性能问题。
【讨论】:
文件描述符与文件指针
文件描述符:
文件描述符是open()系统调用返回的整数值。
int fd = open (filePath, mode);
文件指针:
文件指针是一个指向fopen()库函数返回的C结构体的指针,用于识别文件、包装文件描述符、缓冲功能和I/O操作所需的所有其他功能。文件指针为FILE类型,其定义见"/usr/include/stdio.h"。此定义可能因编译器而异。
FILE *fp = fopen (filePath, mode);
// A FILE Structure returned by fopen
typedef struct
{
unsigned char *_ptr;
int _cnt;
unsigned char *_base;
unsigned char *_bufendp;
short _flag;
short _file;
int __stdioid;
char *__newbase;
#ifdef _THREAD_SAFE
void *_lock;
#else
long _unused[1];
#endif
#ifdef __64BIT__
long _unused1[4];
#endif /* __64BIT__ */
} FILE;
【讨论】:
想添加一些可能有用的点。
关于FILE *
我多次将它用于调试日志。 例如,
FILE *fp;
fp = fopen("debug.txt","a");
fprintf(fp,"I have reached till this point");
fclose(fp);
关于FILE DESCRIPTOR
一般用于IPC。
对 *nix 系统上的文件进行低级控制。(设备、文件、套接字等),因此比 FILE * 更强大。
【讨论】:
fdopen() 做IPC 和带有FILE* 的设备之类的事情吗?
FILE* 设置和初始化 IPC,但您可以从文件描述符 (fdopen()) 创建 FILE*,然后关闭 FILE 也会关闭描述符。因此,您可以做 IPC,但您必须稍微处理文件描述符以方便任何直接IPC。
FILE * 在处理文本文件和用户输入/输出时更有用,因为它允许您使用 API 函数,如sprintf()、sscanf()、fgets()、feof() 等。
文件描述符 API 是低级的,因此它允许使用套接字、管道、内存映射文件(当然还有常规文件)。
【讨论】:
只是结束讨论的注释(如果有兴趣)....
fopen 可能不安全,您可能应该使用设置了独占位的fopen_s 或open。 C1X 提供x 模式,因此您可以在fopen 模式下使用"rx"、"wx" 等模式。
如果您使用open,您可以考虑使用open(..., O_EXCL | O_RDONLY,... ) 或open(..., O_CREAT | O_EXCL | O_WRONLY,... )。
例如,请参阅Do not make assumptions about fopen() and file creation。
【讨论】:
fopen_s 似乎不适用于POSIX,我认为最便携的灵魂是open(2),然后是fdopen(2)。 (把窗户放在一边)。另外,fopen_s() 或 open(2) 后跟 fdopen(2) 哪个更快?
我找到了一个很好的资源here,对两者之间的差异进行了高级概述:
当您想要对文件进行输入或输出时,您可以选择两种基本机制来表示程序与文件之间的连接:文件描述符和流。文件描述符表示为 int 类型的对象,而流表示为 FILE * 对象。
文件描述符为输入和输出操作提供了一个原始的低级接口。文件描述符和流都可以表示与设备(例如终端)的连接,或用于与另一个进程通信的管道或套接字,以及普通文件。但是,如果您想执行特定于特定类型设备的控制操作,则必须使用文件描述符;没有以这种方式使用流的设施。如果您的程序需要在特殊模式下进行输入或输出,例如非阻塞(或轮询)输入(参见文件状态标志),您还必须使用文件描述符。
流提供了一个更高级别的接口,位于原始文件描述符设施之上。流接口对待所有类型的文件都非常相似——唯一的例外是您可以选择的三种缓冲样式(请参阅流缓冲)。
使用流接口的主要优点是用于在流上执行实际输入和输出操作(相对于控制操作)的函数集比文件描述符的相应工具要丰富和强大得多。文件描述符接口只提供了简单的字符块传输函数,但流接口还提供了强大的格式化输入和输出函数(printf 和 scanf)以及面向字符和行的输入和输出函数。
由于流是根据文件描述符实现的,因此您可以从流中提取文件描述符并直接对文件描述符执行低级操作。您还可以最初将连接作为文件描述符打开,然后创建与该文件描述符关联的流。
一般来说,你应该坚持使用流而不是文件描述符,除非你想做一些只能在文件描述符上完成的特定操作。如果您是初级程序员并且不确定要使用哪些函数,我们建议您专注于格式化输入函数(参见格式化输入)和格式化输出函数(参见格式化输出)。
如果您担心程序在 GNU 以外的系统上的可移植性,您还应该注意文件描述符不像流那样可移植。您可以期望任何运行 ISO C 的系统都支持流,但非 GNU 系统可能根本不支持文件描述符,或者可能只实现对文件描述符进行操作的 GNU 函数的子集。然而,GNU C 库中的大多数文件描述符函数都包含在 POSIX.1 标准中。
【讨论】:
系统调用大多使用文件描述符,例如read 和write。库函数将使用文件指针(printf,scanf)。但是,库函数仅使用内部系统调用。
【讨论】: