C includes 2 methods for saving the position of a stream and setting it later. Using fseek() and ftell():

long pos = ftell(file);
// ...
fseek(file, pos, SEEK_SET);

Or fgetpos() and fsetpos():

fpos_t pos;
fgetpos(file, &pos);
fsetpos(file, &pos);

What is the reason for defining them both, instead of just the former?


2 Answers 2


They exist for historical reasons.

Originally there was just ftell() and fseek(). This was fine for Unix-like systems, since files are just a sequence of bytes. The file position is just an integer offset from the beginning of the file.

But there are some operating systems that represent files in more complex ways, such as a sequence of records. A file position is represented as a record index and the offset into that record. fgetpos() and fsetpos() were added to the language as more generic, portable variants. They use an opaque fpos_t datatype in place of integer offsets to represent the file position.

Even on a common system like Windows, fseek() won't work well with relative positioning when the file is opened in text mode. In text mode, the \r\n newline sequence in the file is converted to \n when reading, but fseek() doesn't adjust for that when using whence=SEEK_CUR. That's why using SEEK_CUR is defined only when the stream is open in binary mode.

fseek() and ftell() were kept in the language for backward compatibility. Applications that don't intend to be portable beyond POSIX-like systems can use this simpler interface. Furthermore, fsetpos() can't do relative positioning, it can only set the position to one that was previously returned by fgetpos().

  • $\begingroup$ What happens if I (try to) use ftell()/fseek() on said operating systems that represent files in more complex ways? $\endgroup$
    – CPlus
    Commented Sep 9, 2023 at 5:06
  • $\begingroup$ Unpredictable results. For instance, if you try to use fseek() on a text stream on Windows, it won't adjust for the fact that a newline takes two bytes \r\n. $\endgroup$
    – Barmar
    Commented Sep 9, 2023 at 5:09
  • $\begingroup$ Are there any filesystems that still support a sequence of records like this? Is there a modern use case for that? I am thinking that it could make sense for something like /proc. Do you have any examples of the historical filesystems that used a sequence of records that way and motivated this? $\endgroup$ Commented Sep 15, 2023 at 11:41
  • $\begingroup$ @BruceAdams Probably not these days, because of the Unix influence. But traditionally record-oriented filesystems were common. You could probably get more information about them on Retrocomputing. $\endgroup$
    – Barmar
    Commented Sep 15, 2023 at 14:37

Even on systems where a file is just a "sequence of bytes", ftell/fseek have an obvious limitation in that the file position is represented by a long int, and so cannot exceed the value of LONG_MAX, which is commonly 2 GiB on 16- and 32-bit implementations. On such platforms, this effectively imposes a 2 GiB file size limit on applications that use fseek/ftell, even if the OS is otherwise able to support larger files. This can't be fixed without redefining long int as a larger type, which is ABI-breaking and may have major performance implications.

But such a system could define fpos_t as a 64-bit integer, and then programs using fgetpos/fsetpos could support larger files without interfering with long int. The downside is that fpos_t is not guaranteed to be an integer type, so you can no longer portably seek to arbitrary absolute positions, nor do arithmetic on positions; you can only seek back to positions previously reached and recorded with fgetpos.

POSIX instead introduced ftello/fseeko which takes an off_t, which is guaranteed to be an integer type and may be larger than long int.

  • $\begingroup$ Couldn't they have made fseek()/ftell() use long long int? And I am fairly sure no one will ever be dealing with 9 exabyte files in the foreseeable future. $\endgroup$
    – CPlus
    Commented Sep 10, 2023 at 4:41
  • 2
    $\begingroup$ @user16217248: No, fseek/ftell existed in the C standard (C89) before long long int (C99). C99 could in principle have redefined the interface of fseek/ftell but that would also be ABI breaking. $\endgroup$ Commented Sep 10, 2023 at 4:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .