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

Posted

技术标签:

【中文标题】文件描述符和文件指针有啥区别?【英文标题】:What's the difference between a file descriptor and file pointer?文件描述符和文件指针有什么区别? 【发布时间】:2011-01-26 06:55:11 【问题描述】:

我想知道文件描述符和文件指针的区别。

另外,在什么情况下您会使用其中一个而不是另一个?

【问题讨论】:

gnu.org/software/libc/manual/html_node/… 【参考方案1】:

文件描述符是一个低级整数“句柄”,用于在 Linux 和其他类 Unix 系统中识别内核级别打开的文件(或套接字,或其他)。

您将“裸”文件描述符传递给实际的 Unix 调用,例如 @987654321@@987654322@ 等。

FILE 指针是 C 标准库级别的构造,用于表示文件。 FILE 包装了文件描述符,并添加了缓冲和其他功能以使 I/O 更容易。

您将FILE 指针传递给标准C 函数,例如@987654323@@987654324@

【讨论】:

@nvl: fildes 肯定适用于 Windows,例如msdn.microsoft.com/en-us/library/z0kc8e3z%28VS.80%29.aspx @unwind 您所说的“裸”文件描述符是什么意思?链接参考表明fdread() 的第一个参数。为什么叫裸体? @Geek 与标准库的FILE * 类型相比,整数文件描述符是“较少包装”的,即“裸”。 虽然此评论听起来很权威(而且很可能是),但我发现 Suraj Jain 和 Vogeesh HT 的 cmets 提供的信息更丰富,并且提供了其他 cmets 所没有的必要级别的详细信息。跨度> 【参考方案2】:

一个被缓冲(FILE *),另一个没有。在实践中,您几乎总是希望在从“真实”文件(即驱动器上)读取时使用FILE *,除非您知道自己在做什么或者除非您的文件实际上是一个套接字左右..

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

【讨论】:

+1 用于指出 fileno(),手册页的组织使得这一页很难找到。 fdopen() 也一样。【参考方案3】:

文件描述符只是一个整数,您可以从 POSIX open() 调用中获得。使用标准 C fopen() 你会得到一个 FILE 结构体。 FILE 结构包含此文件描述符以及其他内容,例如文件结尾和错误指示符、流位置等。

因此,与open() 相比,使用fopen() 可以为您提供一定程度的抽象。一般来说,您应该使用fopen(),因为它更便于移植,并且您可以使用使用FILE 结构的所有其他标准C 函数,即fprintf() 和家族。

两者都没有性能问题。

【讨论】:

+1 用于提高便携性。 FILE 是标准 C 库的一部分(回到 C89/C90);文件描述符不是。【参考方案4】:

文件描述符与文件指针

文件描述符:

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

int fd = open (filePath, mode);

    低/内核级处理程序。 传递给 UNIX 系统调用的 read() 和 write()。 不包括缓冲等功能。 便携性较差,效率低下。

文件指针:

文件指针是一个指向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;
    是高级接口。 传递给 fread() 和 fwrite() 函数。 包括缓冲、错误指示和EOF检测等。 提供更高的便携性和效率。

【讨论】:

你能支持那个更高效率的说法吗?我从来没有听说过。 “效率”声明可能是因为缓冲。使用文件描述符,每个 read() 或 write() 都是一个系统调用,每个系统调用都应该被认为是昂贵的。使用 FILE*,缓冲意味着一些读取和写入不会是系统调用。【参考方案5】:

想添加一些可能有用的点。

关于FILE *

    不能用于进程间通信 (IPC)。 在需要通用缓冲 I/O 时使用它。(printf,frpintf,snprintf,scanf)

    我多次将它用于调试日志。 例如,

                 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。【参考方案6】:

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

文件描述符 API 是低级的,因此它允许使用套接字、管道、内存映射文件(当然还有常规文件)。

【讨论】:

+1 因为您添加了内存映射文件,因为在我目前的阅读中,其他答案已经提供。【参考方案7】:

只是结束讨论的注释(如果有兴趣)....

fopen 可能不安全,您可能应该使用设置了独占位的fopen_sopen。 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) 哪个更快?【参考方案8】:

我找到了一个很好的资源here,对两者之间的差异进行了高级概述:

当您想要对文件进行输入或输出时,您可以选择两种基本机制来表示程序与文件之间的连接:文件描述符和流。文件描述符表示为 int 类型的对象,而流表示为 FILE * 对象。

文件描述符为输入和输出操作提供了一个原始的低级接口。文件描述符和流都可以表示与设备(例如终端)的连接,或用于与另一个进程通信的管道或套接字,以及普通文件。但是,如果您想执行特定于特定类型设备的控制操作,则必须使用文件描述符;没有以这种方式使用流的设施。如果您的程序需要在特殊模式下进行输入或输出,例如非阻塞(或轮询)输入(参见文件状态标志),您还必须使用文件描述符。

流提供了一个更高级别的接口,位于原始文件描述符设施之上。流接口对待所有类型的文件都非常相似——唯一的例外是您可以选择的三种缓冲样式(请参阅流缓冲)。

使用流接口的主要优点是用于在流上执行实际输入和输出操作(相对于控制操作)的函数集比文件描述符的相应工具要丰富和强大得多。文件描述符接口只提供了简单的字符块传输函数,但流接口还提供了强大的格式化输入和输出函数(printf 和 scanf)以及面向字符和行的输入和输出函数。

由于流是根据文件描述符实现的,因此您可以从流中提取文件描述符并直接对文件描述符执行低级操作。您还可以最初将连接作为文件描述符打开,然后创建与该文件描述符关联的流。

一般来说,你应该坚持使用流而不是文件描述符,除非你想做一些只能在文件描述符上完成的特定操作。如果您是初级程序员并且不确定要使用哪些函数,我们建议您专注于格式化输入函数(参见格式化输入)和格式化输出函数(参见格式化输出)。

如果您担心程序在 GNU 以外的系统上的可移植性,您还应该注意文件描述符不像流那样可移植。您可以期望任何运行 ISO C 的系统都支持流,但非 GNU 系统可能根本不支持文件描述符,或者可能只实现对文件描述符进行操作的 GNU 函数的子集。然而,GNU C 库中的大多数文件描述符函数都包含在 POSIX.1 标准中。

【讨论】:

【参考方案9】:

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

【讨论】:

我不知道你为什么说库函数只使用内部系统调用:如果你的意思是标准 CI/O(或任何其他)函数,我不是确定这是(普遍?)真的。否则,那不是您所说的,所以我希望您帖子中的语言稍微清理一下。最后一句话让我很困惑。

以上是关于文件描述符和文件指针有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

Linux--IO

typescript 访问修饰符和 javascript 访问修饰符有啥区别?在使用打字稿时我应该更喜欢哪一个?

Linux基础IO篇

C语言中回车符和回车换行符有啥区别

函数库调用和系统调用的区别

第三章文件的描述符和重定向