open()、_open() 和 fopen() 在 MSVC 编译器方面的区别?

smw*_*dia 6 posix visual-c++ clib

我发现这三个函数都与打开文件有关。

打开

此 POSIX 函数已弃用。请改用符合 ISO C++ 标准的 _open。

_打开

打开一个文件。这些函数已被弃用,因为有更安全的版本可用;参见 _sopen_s、_wsopen_s。

打开

打开一个文件。这些函数有更安全的版本,可以执行额外的参数验证并返回错误代码;参见 fopen_s、_wfopen_s。

那么,为什么是他们三个呢?什么时候用哪个?我认为 POSIX 很好,但为什么 MSDN 说 POSIX 版本open已弃用?是否有与前导下划线相关的命名约定,以便我可以根据第一眼的外观选择正确的函数?

当我查看ACPICA 代码时,我看到下面的代码: 似乎该_XXX版本可以禁用某些MS 语言扩展,这些扩展到底是什么?

/*
 * Map low I/O functions for MS. This allows us to disable MS language
 * extensions for maximum portability.
 */
#define open            _open
#define read            _read
#define write           _write
#define close           _close
#define stat            _stat
#define fstat           _fstat
#define mkdir           _mkdir
#define snprintf        _snprintf
#if _MSC_VER <= 1200 /* Versions below VC++ 6 */
#define vsnprintf       _vsnprintf
#endif
#define O_RDONLY        _O_RDONLY
#define O_BINARY        _O_BINARY
#define O_CREAT         _O_CREAT
#define O_WRONLY        _O_WRONLY
#define O_TRUNC         _O_TRUNC
#define S_IREAD         _S_IREAD
#define S_IWRITE        _S_IWRITE
#define S_IFDIR         _S_IFDIR
Run Code Online (Sandbox Code Playgroud)

添加1

看来单下划线前缀_XXX是微软的约定。比如_DEBUG_CrtSetDbgFlag以及前面提到的_open一些来自MSDN的引用:

在 Microsoft C++ 中,带有两个前导下划线的标识符是为编译器实现保留的。因此,Microsoft 的约定是在 Microsoft 特定关键字之前添加双下划线。这些词不能用作标识符名称。

默认情况下启用 Microsoft 扩展。为了确保您的程序完全可移植,您可以通过在编译期间指定 ANSI 兼容 /Za 命令行选项(编译为 ANSI 兼容性)来禁用 Microsoft 扩展。当您执行此操作时,Microsoft 特定的关键字将被禁用。

启用 Microsoft 扩展后,您可以在程序中使用 Microsoft 特定的关键字。为了符合 ANSI 标准,这些关键字以双下划线开头。为了向后兼容,支持除 __except、__finally、__leave 和 __try 之外的所有双下划线关键字的单下划线版本。此外,__cdecl 可以不带前导下划线。

__asm 关键字取代了 C++ asm 语法。asm 是为了与其他 C++ 实现兼容而保留的,但并未实现。使用__asm。

__based 关键字对于 32 位和 64 位目标编译的用途有限。

虽然根据上面的引用,__int64_int64应该都可以工作,但是 Visual Studio 没有为_int64. 但_int64也可以编译。

添加2

snprintf() 和 _snprintf()

Die*_*Epp 9

  • 就Windows而言,打开文件的函数是CreateFile. 这将返回 aHANDLE并且由 Kernel32.dll 提供,而不是由 Visual Studio 提供。可以HANDLE传递给其他 Windows API 函数。

  • 和函数是 POSIX 兼容性函数,可帮助您在 Windows 上编译为 POSIX(Linux、macOS、BSD、Solaris 等)编写的程序_openopen这些函数由 Visual Studio 的 C 运行时定义,并且可能CreateFile在内部调用。该函数的 POSIX 名称是open,但此处定义的函数是为了以防_open万一您已经open在代码中定义了一个名为的函数。该函数返回一个int可以传递给其他 POSIX 函数的值。在 Windows 上,该接口是 Visual Studio 提供的兼容性 API,但在 Linux 和 macOS 上,该接口是操作系统的直接接口,就像HANDLE在 Windows 上一样。

  • fopen函数是 C 标准的一部分。它由 Visual Studio 的 C 运行时定义,并且可能CreateFile在内部调用。它返回一个FILE *可以传递给 C 标准定义的其他函数的值。

因此,总结一下选项:

  • 如果您需要直接使用 Windows API,例如调用GetFileInformationByHandleCreateFileMapping,则需要 aHANDLE并且您可能应该调用CreateFile来打开文件。

  • 如果您有一个已经为 POSIX 系统编写的程序,那么您可以使用open来更轻松地将您的程序移植到 Windows。如果您只为 Windows 编写,那么使用此界面没有任何优势。

  • 如果您的程序只需要执行打开、读取和写入等基本文件操作,那么fopen就足够了,并且它也可以在其他系统上运行。AFILE *可以(通常是)由您的应用程序缓冲,并支持fprintffscanf、 和 等便捷操作fgets。如果你想调用orfgets返回的文件,你必须自己编写它。CreateFileopen

可以将文件句柄从一种 API 转换为另一种 API,但您必须注意所有权问题。“所有权”实际上并不是一个技术概念,它只是描述了谁负责管理对象的状态,并且您希望避免破坏不属于您的对象,并避免同一个对象有多个所有者。

  • 对于 Windows API,您可以使用从 a_open_osfhandle()创建,并从获取。然而,在这两种情况下,句柄都将归.FILE *HANDLE_get_osfhandle()HANDLEFILE *FILE *

  • 对于 POSIX API,您可以使用从文件描述符fdopen()创建,也可以使用从获取文件描述符。同样,在这两种情况下,文件都属于.FILE *intfileno()intFILE *FILE *

请注意,由于 Windows 文件名是 . 的数组wchar_t,而 macOS / Linux 等文件名是char.

如果您使用不同的 C 运行时(例如 MinGW),或者使用适用于 Linux 的 Windows 子系统,情况将会有所不同。