cwindowsftell

ftell() no more returning the correct offset on a text file with Windows 11 Enterprise


A few weeks ago (August 7th, 2025) an old C program started corrupting its output file and after investigation, I found the ftell() function is no more returning the correct offset in a text file.

I replicated the issue in Linux and in Windows with the following example:

#include <stdio.h>

int main()
{
    /* Opening file in read mode */
    FILE *fp = fopen("g4g.txt", "r");

    /* Reading first string */
    char string[20];
    fscanf(fp, "%s", string);

    /* Printing position of file pointer */
    printf("{%s}, %ld\n", string, ftell(fp));
    return 0;
}

Contents of the file g4g.txt:

Someone over there is calling you. We are going for work. Take care of yourself.

Source of example

I expect the output to be Someone, 7 because the word read from the file by fscanf() has 7 characters and ftell() returns a value that should allow fseek() to position the file offset just after this word.

In Linux, the output is Someone, 7 as expected, but on my Windows laptop, the output is Someone, 6. I asked a coworker to try it on his laptop and the output was Someone, 7 as expected.

The problem has been propagating slowly to other users after 1 or 2 weeks; now we are 3 to have this problem.


Here are the setups and results for Cygwin64 (gcc and mingw) and VisualStudio:

Setup
Cygwin 64 with gcc & mingw32
$ cygcheck -c cygwin; gcc -v; i686-w64-mingw32-gcc -v
Cygwin Package Information
Package              Version    Status
cygwin               3.6.4-1        OK
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-cygwin/13/lto-wrapper.exe
Target: x86_64-pc-cygwin
Configured with: /mnt/share/packages/gccmake/gcc-13/gcc/gcc.x86_64/src/gcc-13.4.0/configure --srcdir=/mnt/share/packages/gccmake/gcc-13/gcc/gcc.x86_64/src/gcc-13.4.0 --prefix=/usr --exec-prefix=/usr --localstatedir=/var --sysconfdir=/etc --docdir=/usr/share/doc/gcc --htmldir=/usr/share/doc/gcc/html -C --build=x86_64-pc-cygwin --host=x86_64-pc-cygwin --target=x86_64-pc-cygwin --without-libiconv-prefix --without-libintl-prefix --libexecdir=/usr/lib --with-gcc-major-version-only --enable-shared --enable-shared-libgcc --enable-static --enable-version-specific-runtime-libs --enable-bootstrap --enable-__cxa_atexit --enable-clocale=newlib --with-dwarf2 --with-tune=generic --enable-languages=ada,c,c++,fortran,lto,objc,obj-c++,jit --enable-graphite --enable-threads=posix --enable-libatomic --enable-libgomp --enable-libquadmath --enable-libquadmath-support --disable-libssp --enable-libada --disable-symvers --disable-multilib --with-gnu-ld --with-gnu-as --with-cloog-include=/usr/include/cloog-isl --without-libiconv-prefix --without-libintl-prefix --with-system-zlib --enable-linker-build-id --with-default-libstdcxx-abi=gcc4-compatible --enable-libstdcxx-filesystem-ts
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 13.4.0 (GCC)
Using built-in specs.
COLLECT_GCC=i686-w64-mingw32-gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-w64-mingw32/13/lto-wrapper.exe
Target: i686-w64-mingw32
Configured with: /mnt/share/cygpkgs/mingw64-i686-gcc/mingw64-i686-gcc.x86_64/src/gcc-13.4.0/configure --srcdir=/mnt/share/cygpkgs/mingw64-i686-gcc/mingw64-i686-gcc.x86_64/src/gcc-13.4.0 --prefix=/usr --exec-prefix=/usr --localstatedir=/var --sysconfdir=/etc --docdir=/usr/share/doc/mingw64-i686-gcc --htmldir=/usr/share/doc/mingw64-i686-gcc/html -C --build=x86_64-pc-cygwin --host=x86_64-pc-cygwin --target=i686-w64-mingw32 --without-libiconv-prefix --without-libintl-prefix --with-sysroot=/usr/i686-w64-mingw32/sys-root --with-build-sysroot=/usr/i686-w64-mingw32/sys-root --disable-multilib --disable-win32-registry --enable-languages=c,c++,fortran,lto,objc,obj-c++ --enable-fully-dynamic-string --enable-graphite --enable-libgomp --enable-libquadmath --enable-libquadmath-support --enable-libssp --enable-version-specific-runtime-libs --with-dwarf2 --with-gnu-ld --with-gnu-as --with-gcc-major-version-only --with-tune=generic --with-cloog-include=/usr/include/cloog-isl --with-system-zlib --enable-threads=posix --libexecdir=/usr/lib
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 13.4.0 (GCC)
Ubuntu 24
~$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 13.3.0-6ubuntu2~24.04' --with-bugurl=file:///usr/share/doc/gcc-13/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-13 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-13-fG75Ri/gcc-13-13.3.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-13-fG75Ri/gcc-13-13.3.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04)
~$ uname -r
6.14.0-29-generic

First test inside Cygwin64
With gcc
gcc ftell.c
./a.exe
Result:
{Someone}, 7

With mingw32-w64-gcc
i686-w64-mingw32-gcc ftell.c
./a.exe
Result:
{Someone}, 6

With Visual Studio 2022
  >"c:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\bin\Hostx86\x64\cl.exe" /TC ftell.c
  Microsoft (R) C/C++ Optimizing Compiler Version 19.44.35215 for x64
  Copyright (C) Microsoft Corporation.  All rights reserved.

  ftell.c
  Microsoft (R) Incremental Linker Version 14.44.35215.0
  Copyright (C) Microsoft Corporation.  All rights reserved.

  /out:ftell.exe
  ftell.obj

  >ftell.exe
  {Someone}, 6


Ubuntu 24
gcc ftell.c
./a.out
Result:
{Someone}, 7

Solution

  • The ftell function was not designed to return the actual offset from text files.

    Section 7.21.9.4p2 of the C11 standard regarding the ftell function states:

    For a text stream, its file position indicator contains unspecified information, usable by the fseek function for returning the file position indicator for the stream to its position at the time of the ftell call; the difference between two such return values is not necessarily a meaningful measure of the number of characters written or read.

    Additionally, the Microsoft documentation for ftell states:

    The value returned by ftell and _ftelli64 may not reflect the physical byte offset for streams opened in text mode, because text mode causes carriage return-line feed translation. Use ftell with fseek or _ftelli64 with _fseeki64 to return to file locations correctly.

    What this means is that if you're using ftell for anything other that passing to fseek, you can't expect any particular result.

    A common (but incorrect) usage of ftell is to determine the size of a file. The proper way to do this is with the stat function on Linux or the _stat or _stat64 functions on Windows.

    struct stat finfo;
    stat("g4g.txt", &finfo);
    printf("size=%jd\n", (intmax_t)finfo->st_size);