<p>C includes 2 methods for saving the position of a stream and setting it later. Using <code>fseek()</code> and <code>ftell()</code>:</p>
<pre><code>long pos = ftell(file);
// ...
fseek(file, pos, SEEK_SET);
</code></pre>
<p>Or <code>fgetpos()</code> and <code>fsetpos()</code>:</p>
<pre><code>fpos_t pos;
fgetpos(file, &amp;pos);
//
fsetpos(file, &amp;pos);
</code></pre>
<p>What is the reason for defining them both, instead of just the former?</p>

<c><standard-library>
Why does the C library include fgetpos()/fsetpos() if the same functionality can be achieved with ftell()/fseek()?
<p>Even on systems where a file <em>is</em> just a &quot;sequence of bytes&quot;, <code>ftell/fseek</code> have an obvious limitation in that the file position is represented by a <code>long int</code>, and so cannot exceed the value of <code>LONG_MAX</code>, 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 <code>long int</code> as a larger type, which is ABI-breaking and may have major performance implications.</p>
<p>But such a system could define <code>fpos_t</code> as a 64-bit integer, and then programs using <code>fgetpos/fsetpos</code> could support larger files without interfering with <code>long int</code>.  The downside is that <code>fpos_t</code> 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 <code>fgetpos</code>.</p>
<p>POSIX instead introduced <code>ftello/fseeko</code> which takes an <code>off_t</code>, which is guaranteed to be an integer type and may be larger than <code>long int</code>.</p>

CC BY-SA 4.0
2023-09-10T04:14:23.623Z
2023-09-10T04:14:23.623Z
Nate Eldredge
CC BY-SA 4.0
2023-09-10T04:41:07.743Z
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.
CPlus
CC BY-SA 4.0
2023-09-10T04:46:13.810Z
@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.
Nate Eldredge
<p>They exist for historical reasons.</p>
<p>Originally there was just <code>ftell()</code> and <code>fseek()</code>. 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.</p>
<p>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. <code>fgetpos()</code> and <code>fsetpos()</code> were added to the language as more generic, portable variants. They use an opaque <code>fpos_t</code> datatype in place of integer offsets to represent the file position.</p>
<p>Even on a common system like Windows, <code>fseek()</code> won't work well with relative positioning when the file is opened in text mode. In text mode, the <code>\r\n</code> newline sequence in the file is converted to <code>\n</code> when reading, but <code>fseek()</code> doesn't adjust for that when using <code>whence=SEEK_CUR</code>. That's why using <code>SEEK_CUR</code> is defined only when the stream is open in binary mode.</p>
<p><code>fseek()</code> and <code>ftell()</code> 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, <code>fsetpos()</code> can't do relative positioning, it can only set the position to one that was previously returned by <code>fgetpos()</code>.</p>

CC BY-SA 4.0
2023-09-09T05:04:45.873Z
2023-09-09T06:31:07.813Z
2023-09-09T06:31:07.813Z
Barmar
CC BY-SA 4.0
2023-09-09T05:06:03.313Z
What happens if I (try to) use `ftell()`/`fseek()` on said operating systems that represent files in more complex ways?
CPlus
CC BY-SA 4.0
2023-09-09T05:09:45.787Z
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`.
Barmar
CC BY-SA 4.0
2023-09-15T11:41:22.530Z
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?
Bruce Adams
CC BY-SA 4.0
2023-09-15T14:37:03.330Z
@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.se].
Barmar
CC BY-SA 4.0
2024-07-28T05:17:18.527Z
@BruceAdams IBM mainframe operating systems such as z/OS support record-oriented files, and even extend stdio with functions to delete individual records (`fdelrec`), search for them by key (`flocate`), etc. Another system with record-oriented files is OpenVMS. On such systems, locating a record can be more complex than just a single integer; some older storage formats on z/OS actually use CHS-style physical disk addresses; for indexed files, the record identifier might even be a multibyte key
Simon Kissane
# Why does the C library include fgetpos()/fsetpos() if the same functionality can be achieved with ftell()/fseek()?
Posted by: username_0 on 2023-09-09
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?
**Tags:** c standard-library
---
## 2 answers
### Answer by username_2 on 2023-09-09. Score: 7 | Accepted answer
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()`.
> **Comments:**
> **username_0** (2023-09-09): What happens if I (try to) use `ftell()`/`fseek()` on said operating systems that represent files in more complex ways?  
> **username_2** (2023-09-09): 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`.  
> **username_3** (2023-09-15): 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?  
> **username_2** (2023-09-15): @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.se].  
> **username_4** (2024-07-28): @BruceAdams IBM mainframe operating systems such as z/OS support record-oriented files, and even extend stdio with functions to delete individual records (`fdelrec`), search for them by key (`flocate`), etc. Another system with record-oriented files is OpenVMS. On such systems, locating a record can be more complex than just a single integer; some older storage formats on z/OS actually use CHS-style physical disk addresses; for indexed files, the record identifier might even be a multibyte key  
---
### Answer by username_1 on 2023-09-10. Score: 3
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`.
> **Comments:**
> **username_0** (2023-09-10): 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.  
> **username_1** (2023-09-10): @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.  
---
