3

I see these 3 functions are all related to opening a file.

open:

This POSIX function is deprecated. Use the ISO C++ conformant _open instead.

_open:

Opens a file. These functions are deprecated because more-secure versions are available; see _sopen_s, _wsopen_s.

fopen:

Opens a file. More-secure versions of these functions that perform additional parameter validation and return error codes are available; see fopen_s, _wfopen_s.

So, why there are three of them? When to use which? I thought POSIX is good but why MSDN says the POSIX version of open is deprecated? And is there any naming convention related to the leading underscore so I can pick the right function based its first looking?

And when I am looking into the ACPICA code, I see below code: It seems the _XXX version can disable some MS language extensions, what exactly are these extensions?

/*
 * 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

ADD 1

It seems the single underscore prefix _XXX is a Microsoft convention. Such as _DEBUG, _CrtSetDbgFlag, and the aforementioned _open. Some quote from the MSDN:

In Microsoft C++, identifiers with two leading underscores are reserved for compiler implementations. Therefore, the Microsoft convention is to precede Microsoft-specific keywords with double underscores. These words cannot be used as identifier names.

Microsoft extensions are enabled by default. To ensure that your programs are fully portable, you can disable Microsoft extensions by specifying the ANSI-compatible /Za command-line option (compile for ANSI compatibility) during compilation. When you do this, Microsoft-specific keywords are disabled.

When Microsoft extensions are enabled, you can use the Microsoft-specific keywords in your programs. For ANSI compliance, these keywords are prefaced by a double underscore. For backward compatibility, single-underscore versions of all the double-underscored keywords except __except, __finally, __leave, and __try are supported. In addition, __cdecl is available with no leading underscore.

The __asm keyword replaces C++ asm syntax. asm is reserved for compatibility with other C++ implementations, but not implemented. Use __asm.

The __based keyword has limited uses for 32-bit and 64-bit target compilations.

Though according to above quote, __int64 and _int64 should both work, but Visual Studio provide NO syntax highlight for _int64. But _int64 can compile, too.

ADD 2

snprintf() and _snprintf()

smwikipedia
  • 61,609
  • 92
  • 309
  • 482
  • Your are compile your file in C++. You must compile your file by telling to VC to compile as C. `open()` is not deprecated in POSIX. – Stargateur Jun 28 '17 at 02:13
  • 1
    Seem that windows is windows... they don't want people use `open()` from POSIX. So let say that MSVC is not POSIX compliant. – Stargateur Jun 28 '17 at 02:25
  • 1
    1) This is not about C, but additional library functions. 2) They are correct in that aspect as `open` could clash with user names. `_open` uses implementation-reserved names. 3) `open` is POSIX standard, so if you write POSIX programs, use it. No one uses the MS extensions `_open`, etc. 4) Microsoft is a really bad reference for the C and POSIX standards. They are well known to defy them (e.g. MSVC is not standard compliant **intentionally** since 18 years (C99). Actually they don't even guarntee full C90 compliance, iirc. 5) If you program in C, use a compliant compiler like gcc or clang. – too honest for this site Jun 28 '17 at 02:43

1 Answers1

6
  • As far as Windows is concerned, the function for opening files is CreateFile. This returns a HANDLE and is provided by Kernel32.dll, not by Visual Studio. The HANDLE can be passed to other Windows API functions.

  • The _open and open functions are POSIX compatibility functions to help you compile programs written for POSIX (Linux, macOS, BSD, Solaris, etc.) on Windows. These functions are defined by Visual Studio's C runtime, and presumably, they call CreateFile internally. The POSIX name of the function is open, but the function here is defined as _open in case you have already defined a function named open in your code. The function returns an int which can be passed to other POSIX functions. On Windows, this interface is a compatibility API provided by Visual Studio, but on Linux and macOS, this interface is the direct interface for the operating system, just like HANDLE on Windows.

  • The fopen function is part of the C standard. It is defined by Visual Studio's C runtime, and presumably, calls CreateFile internally. It returns a FILE * which can be passed to other functions defined by the C standard.

So, to summarize the options:

  • If you need to use the Windows API directly, like calling GetFileInformationByHandle or CreateFileMapping, you need a HANDLE and you should probably call CreateFile to open files.

  • If you have a program which is already written for POSIX systems, then you can use open to make it easier to port your program to Windows. If you are only writing for Windows, there are no advantages to using this interface.

  • If your program only needs to do basic file operations like opening, reading, and writing, then fopen is sufficient and it will also work on other systems. A FILE * can be (and usually is) buffered by your application and supports convenient operations like fprintf, fscanf, and fgets. If you want to call fgets on a file returned by CreateFile or open you will have to write it yourself.

It's possible to convert file handles from one API to the other, but you have to pay attention to ownership issues. "Ownership" is not really a technical concept, it just describes who is responsible for managing the state of an object, and you want to avoid destroying objects that you don't own, and avoid having multiple owners for the same object.

  • For the Windows API, you can use _open_osfhandle() to create a FILE * from a HANDLE, and _get_osfhandle() to get the HANDLE from the FILE *. However, in both cases, the handle will be owned by the FILE *.

  • For the POSIX API, you can use fdopen() to create a FILE * from a int file descriptor, and you can use fileno() to get the int file descriptor from a FILE *. Again, in both cases the file is owned by the FILE *.

Note that portability is complicated by the fact that Windows filenames are arrays of wchar_t, but macOS / Linux / etc. filenames are arrays of char.

If you use a different C runtime like MinGW, or if you use the Windows Subsystem for Linux, things will be different.

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • Note: this is possible to use `fdopen()` so you can use `open()` and `fgets()` in the same time (could be possible to create a socket and transform it as `FILE` on linux). – Stargateur Jun 28 '17 at 03:25
  • Unfortunately, the question is about Windows and it is impossible to fdopen() a socket on Windows. – Dietrich Epp Jun 28 '17 at 03:34
  • Sorry, I'm not clear this was just a use case of `fdopen()`. Because I don't have other example, you say "If you want to call fgets on a file returned by CreateFile or open you will have to write it yourself.", but this is false you can use `fdopen()` with `open()`. Or maybe windows don't implement this ? – Stargateur Jun 28 '17 at 03:36
  • @Stargateur: Yes, that's true, you can use `fdopen()` to get a `FILE *` from a `int` file descriptor, and you likewise you can use `_open_osfhandle()` to get a `FILE *` from a `HANDLE` for a file. The important part to remember here is that these are three *different* APIs for working for files. The `HANDLE` is part of the API for communicating with Windows itself. The `FILE *` is part of the API for using code in the standard C library, which is presumably a wrapper around a `HANDLE` with a buffer. The `int` is part of the POSIX API, which is a compatibility layer on windows, unbuffered. – Dietrich Epp Jun 28 '17 at 04:31
  • @Stargateur: If you want to use `fdopen()`, `fileno()`, `_open_osfhandle()`, or `_get_osfhandle()` you will have to understand the buffering provided by the `FILE *` interface and the consequences of functions that take ownership of their parameters. For example, if you `fileno()` and then `fdopen()` you have made a mistake, because the handle is now owned by two different `FILE *` objects. In general, it is simpler to pick one API and stick with it, and the main motivation is whether you want buffering and whether you want to e.g. use memory maps or stat files. – Dietrich Epp Jun 28 '17 at 04:36