文件描述符和文件指针有什么区别?

153

文件描述符和文件指针有什么关系?何时适合使用每个?


1
https://www.gnu.org/software/libc/manual/html_node/Streams-and-File-Descriptors.html - Suraj Jain
9个回答

184
文件描述符是在Linux和其他类Unix系统中用于标识已打开的文件(或套接字等)的低级整数“句柄”,位于内核级别。您将“裸”文件描述符传递给实际的Unix调用,例如read()write()等。
FILE指针是C标准库级别的构造,用于表示文件。FILE包装文件描述符,并添加缓冲和其他功能,使I/O更容易。您将FILE指针传递给标准C函数,例如fread()fwrite()

@nvl:fildes在Windows上肯定是可用的,例如http://msdn.microsoft.com/en-us/library/z0kc8e3z%28VS.80%29.aspx - kennytm
3
“naked”文件描述符是什么意思?与此链接中提到的内容相似,'fd' 是 'read()' 函数的第一个参数。为什么你会称它为"naked"? - Geek
5
与标准库的 FILE * 类型相比,整数文件描述符更少包装,即更为“裸露”。 - unwind
虽然这个评论听起来很有权威性(而且它可能确实如此),但我发现Suraj Jain和Vogeesh HT的评论更具信息量,并提供了其他评论中没有的必要细节水平。 - Stato Machino

79

其中一个是带缓存的(FILE *),另一个则不带。实际上,当您从“真实”文件(即在驱动器上)中读取时,几乎总是希望使用 FILE *,除非您知道自己在做什么或者文件实际上是一个套接字等。

您可以使用 fileno()FILE * 获取文件描述符,并且可以使用 fdopen() 打开一个带缓冲的 FILE *


18
谢谢指出fileno()函数的用法,由于手册页的组织结构,这个函数很难找到。fdopen()也是一样。 - BD at Rivenhill

31

文件描述符只是从POSIX的open()调用中获取的整数。使用标准C的fopen()可获得FILE结构体返回。除文件描述符外,FILE结构体还包含其他信息,例如文件结束和错误指示器、流位置等。

因此,与open()相比,使用fopen()提供了一定程度的抽象。通常情况下,应该使用fopen(),因为它更具移植性,可以使用所有其他使用FILE结构体的标准C函数,例如fprintf()及其系列函数。

使用任何一个都没有性能问题。


12
感谢您提及可移植性。FILE 是标准 C 库的一部分(追溯至 C89/C90);而文件描述符则不是。 - tomlogic

23

文件描述符 vs 文件指针

文件描述符:

文件描述符是由open()系统调用返回的整数值。

int fd = open (filePath, mode);

  1. 底层/内核级别的句柄。
  2. 传递给UNIX系统调用中的read()和write()。
  3. 不包括缓冲和其他功能。
  4. 可移植性较差且效率低。

文件指针:

文件指针是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;
  1. 它是高级界面。
  2. 传递给fread()和fwrite()函数。
  3. 包括缓冲、错误指示和EOF检测等功能。
  4. 提供更高的可移植性和效率。

1
你能证明那个更高效的说法吗?我从未听说过。 - Gid
1
“效率”声明可能是由于缓冲造成的。使用文件描述符,每个read()或write()都是一个系统调用,而每个系统调用都应被视为昂贵的。使用FILE*,缓冲意味着一些读取和写入不会成为系统调用。 - Mike Spear

12

希望能提供一些可能有用的观点。

关于FILE *

  1. 不能用于进程间通信(IPC)。
  2. 在需要通用缓冲I/O(如printf、fprintf、snprintf、scanf)时使用。
  3. 我经常用它来进行调试记录。例如,

                 FILE *fp;
                 fp = fopen("debug.txt","a");
                 fprintf(fp,"I have reached till this point");
                 fclose(fp);
    

关于文件描述符

  1. 它通常用于进程间通信。

  2. 在*nix系统中,它对文件(设备、文件、套接字等)提供低级别的控制,因此比 FILE * 更强大。


你不能使用 fdopen() 来处理 IPC 和设备,使用 FILE* 吗? - osvein
1
实际上,是肯定的和否定的都有。你不能使用FILE*来设置和初始化IPC,但是你可以通过文件描述符(fdopen())创建一个FILE*,并且稍后关闭FILE也会关闭描述符。因此,你可以IPC,但是你必须处理一些文件描述符以便于任何直接的IPC。 - Micah W

4

FILE *在处理文本文件和用户输入/输出时更加实用,因为它允许您使用API函数,如sprintf()sscanf()fgets()feof()等。

文件描述符API是低级的,因此它允许处理套接字、管道、内存映射文件(当然也包括常规文件)。


1
+1 是因为你添加了内存映射文件,因为根据我目前的阅读,其他答案已经被提供了。 - ernie.cordell

4

这里简要说明一下(如果您感兴趣)...

fopen 可能存在安全问题,建议使用 fopen_s 或带有 exclusive bits 的 open。C1X 提供了 x 模式,可以使用 "rx""wx" 等模式来打开文件。

如果使用 open,可以考虑使用 open(..., O_EXCL | O_RDONLY,... )open(..., O_CREAT | O_EXCL | O_WRONLY,... )

例如,请参见《不要对 fopen() 和文件创建进行假设》


由于'POSIX'中似乎没有提供fopen_s,因此假设最可移植的解决方案是使用open(2)fdopen(2)(不考虑Windows)。另外,哪个更快,fopen_s()还是使用open(2)fdopen(2) - Mihir Luthra

4
我在这里找到了一个很好的资源它提供了关于两者之间区别的高级概述:
当您希望对文件进行输入或输出时,可以选择两种基本机制来表示程序和文件之间的连接:文件描述符和流。文件描述符表示为int类型的对象,而流表示为FILE *对象。
文件描述符提供了一种原始、低级别的接口来执行输入和输出操作。文件描述符和流都可以表示与设备(如终端)、用于与另一个进程通信的管道或套接字以及普通文件的连接。但是,如果要执行针对特定设备的控制操作,则必须使用文件描述符;没有使用流的工具。如果程序需要以非阻塞(或轮询)输入方式进行输入或输出,也必须使用文件描述符(参见文件状态标志)。
流提供了较高级别的接口,在原始文件描述符功能之上进行分层。流接口几乎将所有类型的文件都视为同类——唯一的例外是您可以选择的三种缓冲样式(请参见流缓冲)。
使用流接口的主要优点是,用于实际输入和输出操作(而不是控制操作)的函数集比文件描述符提供的相应工具更为丰富和强大。文件描述符接口仅提供用于传输字符块的简单函数,但流接口还提供了强大的格式化输入和输出函数(printf和scanf),以及用于字符和行导向输入和输出的功能。
由于流是基于文件描述符实现的,因此您可以从流中提取文件描述符,并直接在文件描述符上执行低级操作。您也可以最初将连接作为文件描述符打开,然后创建与该文件描述符关联的流。
一般来说,除非要执行只能在文件描述符上完成的某些特定操作,否则应使用流而不是文件描述符。如果您是初学者程序员,不确定要使用哪些函数,我们建议您重点关注格式化输入函数(请参见格式化输入)和格式化输出函数(请参见格式化输出)。
如果您关心程序在GNU之外的系统上的可移植性,则还应该知道文件描述符不如流具有可移植性。您可以期望运行ISO C的任何系统都支持流,但非GNU系统可能根本不支持文件描述符,或者只实现一部分操作在文件描述符上的GNU函数。然而,在GNU C库中的大多数文件描述符函数均包括在POSIX.1标准中。

2

系统调用主要使用文件描述符,例如readwrite。库函数将使用文件指针(printfscanf)。但是,库函数仅在内部使用系统调用。


我不确定你为什么说库函数只使用内部系统调用:如果你指的是标准C I/O(或任何其他)函数,我不确定这是普遍适用的。否则,那就不是你所说的,所以我希望看到你帖子中的语言得到一些改进。最后一句话让我感到困惑。 - ernie.cordell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接