tags:

views:

801

answers:

6

I'm doing a small project in C after quite a long time away from it. These happen to include some file handling. I noticed in various documentation that there are functions which return FILE * handles and others which return (small integer) descriptors. Both sets of functions offer the same basic services I need so it really does not matter I use.

But I'm curious about the collection wisdom: is it better to use fopen() and friends, or open() and friends?

Edit Since someone mentioned buffered vs unbuffered and accessing devices, I should add that one part of this small project will be writing a userspace filesystem driver under FUSE. So the file level access could as easily be on a device (e.g. a CDROM or a SCSI drive) as on a "file" (i.e. an image).

+2  A: 

usually, you should favor using the standard library (fopen). However, there are occasions where you will need to use open directly.

One example that comes to mind is to work around a bug in an older version of solaris which made fopen fail after 256 files were open. This was because they erroniously used an unsigned char for the fd field in their struct FILE implementation instead of an int. But this was a very specific case.

Evan Teran
That 'older version of Solaris' includes Solaris 10 (current) - at least for 32-bit code.
Jonathan Leffler
+3  A: 

Yes. When you need a low-level handle.

On UNIX operating systems, you can generally exchange file handles and sockets.

Also, low-level handles make for better ABI compatibility than FILE pointers.

Joshua
int fd = fileno(file); /* gets a low level handle from a FILE * */
Evan Teran
NOTE: fileno is posix and not c89/c99 though.
Evan Teran
@Joshua: are you referring to the ability to pass file descriptors between processes (esoteric, but I know of software where it is done), or changing between file pointers and file descriptors, or something else? Remember, fdopen() can convert a file descriptor into a file pointer.
Jonathan Leffler
On the other hand, if you need a file descriptor, you're already outside the realm of standard C, so needing fileno is no problem.
Rob Kennedy
Jonathan, no. Sometimes different compilers have a different idea about FILE and FILE *, but the low-level handle is defined by the OS ABI so your .o file is more likely to work with the .o file from some other compiler.
Joshua
+10  A: 

It is better to use open() if you are sticking to unix-like systems and you might like to:

  • Have more fine-grained control over unix permission bits on file creation.
  • Use the lower-level functions such as read/write/mmap as opposed to the C buffered stream I/O functions.
  • Use file descriptor (fd) based IO scheduling (poll, select, etc.) You can of course obtain an fd from a FILE * using fileno(), but care must be taken not to mix FILE * based stream functions with fd based functions.
  • Open any special device (not a regular file)

It is better to use fopen/fread/fwrite for maximum portability, as these are standard C functions, the functions I've mentioned above aren't.

Chris Young
I'm glad we have someone spelling out the benefits of using open() every once in a while.
Chris Lutz
+4  A: 

fopen works at a higher level than open .... fopen returns you a pointer to FILE stream which is similar to the stream abstraction that you read in C++

open returns you a file descriptor for the file opened ... It does not provide you a stream abstraction and you are responsible for handling the bits and bytes yourself ... This is at a lower level as compared to fopen

Stdio streams are buffered, while open() file descriptors are not. Depends on what you need. You can also create one from the other:

int fileno (FILE * stream) returns the file descriptor for a FILE *, FILE * fdopen(int fildes, const char * mode) creates a FILE * from a file descriptor.

Be careful when intermixing buffered and non-buffered IO, since you'll lose what's in your buffer when you don't flush it with fflush().

lakshmanaraj
+4  A: 

The objection that "fopen" is portable and "open" isn't is bogus.

fopen is part of libc, open is a POSIX system call.

Each is as portable as the place they come from.

i/o to fopen'ed files is (you must assume it may be, and for practical purposes, it is) buffered by libc, file descriptors open()'ed are not buffered by libc (they may well be, and usually are buffered in the filesystem -- but not everything you open() is a file on a filesystem.

What's the point of fopen'ing, for example, a device node like /dev/sg0, say, or /dev/tty0... What are you going to do? You're going to do an ioctl on a FILE *? Good luck with that.

Maybe you want to open with some flags like O_DIRECT -- makes no sense with fopen().

smcameron
A: 

fopen and its cousins are buffered. open, read, and write are not buffered. Your application may or may not care.

fprintf and scanf have a richer API that allows you to read and write formatted text files. read and write use fundamental arrays of bytes. Conversions and formatting must be hand crafted.

The difference between file descriptors and (FILE *) is really inconsequential.

Randy

Randy Stegbauer