Portable C and long
The C standard orginally defined three sizes of integer (apart
from char
): short
, int
and long
. It was stated that int
should
reflect the natural size for a particular machine. ANSI C added the
requirement that short
and int
be at
least 16 bits, and long
at least 32 bits.
One might therefore assume that on any 64 bit
OS, int
would be 64 bits. One would be
wrong. Today int
is almost universally 32 bits,
save on some embedded systems on which 16 bit int
s
might be found.
But then presumably long
will be 64 bits on any
64 bit system? It is on MacOS, Linux and all UNIX derivatives I
am aware of, but it is not on MS Windows. There it is
32 bits still, even on 64 bit platforms, just as it was on
32 bit Windows, and most 32 bit UNIXes.
The 1999 C standard added another integer data
type, long long
. This had been in widespread use
as an extension to the 1989 standard, and the C99 standard specified
that it is at least 64 bits.
So amongst common 64 bit OSes, there are two different
implementations of the sizes of int
, long
and long long
. UNIX-based systems tend to use
length of 4/8/8 (in bytes, as returned by sizeof()
),
whereas Windows uses 4/4/8. In a different terminology, 4/8/8 is
called LP64 (long and pointers 64 bit) and 4/4/8 is LLP64
(long long and pointers 64 bit).
To add to the confusion, I believe that the Cygwin environment on 64 bit Windows is LP64, just as most UNIXes are.
Hardware support for 128 bit integers is common. As one
example, the 32 bit Pentium III introduced it to the IA32
range in 1999, and all IA32 and x86_64 CPUs since have had it (the
`SSE'
extensions). No C compiler I am aware of
has long long
as 128 bit though.
C99 did introduce the int32_t
and int64_t
types. These were marked as optional,
but int_least32_t
and int_least64_t
are
required (smallest integer with at least N bits).
When it matters
I/O
The standard C library functions fseek()
and ftell()
use the type long
for the file
offset. So code using these functions will be unable to cope with
files of more than 2GB on Windows platforms.
Switching to using fsetpos()
and fgetpos()
may not be the answer. These functions return a fpos_t
type, which may well avoid the 2GB limit, but they are not
guaranteed to return an integer. The only thing one can do with the
return value of fgetpos()
is pass it
to fsetpos()
. One cannot modify it, so one
can save one's position, but not seek to somewhere new.
Switching to using fseeko()
and ftello()
may not be the answer. These functions return an offset_t
type, which may well avoid the 2GB limit, but they are not in any C
standard. They are part of the POSIX standard of 2001, but Windows
tends not to support POSIX.
Switching to non-standard extensions such
as _fseeki64()
and _ftelli64()
is never
good for portability. This particular pair exist on Windows, but
not elsewhere.
Neither C99 nor C11 introduces any new functions to help here.
printf
Trying to print pointers or file offsets by casting them
to long
and using a format specifier
of %ld
will not work well on an LLP64 system. Since C99
one can cast to intmax_t
and format with %jd
.