How do headers like C's stdint.h work?
C comes with a header named <stdint.h>
, which defines types like
int64_t
and uint8_t
. But how does that work? How does a header tell
C to “invent” a new type of integer? And what about types like long
and short
?
Under the Hood
A C compiler eventually needs to compile to machine code. Machine code only has hard, fixed-width types like a 32-bit int, 64-bit int etc. (or rather, it has memory blocks of that size + operations that operate on memory of that size and either treat it as signed or unsigned)
The people who created your compiler need to generate these
instructions, so had to hard-code a few built-in numeric types. Let’s
say they called them __int64
, __int32
, __int8
, __int16
,
__uint64
, __uint32
, __uint8
and __uint16
. These are
non-standard type names specific to this compiler.
Unpredictably-sized Types
In addition, they define long
, short
etc. as synonyms for these
types. These are standard type names that have been around since the
birth of C. However, they are not of a fixed size. There are just a few
vague rules how these types must relate to each other.
And not all of them are still followed in practice. E.g. the
recommendation is that int
should be the “native” number type of the
computer’s CPU. The one that’s the fastest. But when modern operating
systems switched from 32-bit to 64-bit CPUs, they didn’t want everyone
to have to rewrite their code. So most 64-bit platforms use a 32-bit
int
, even though a 64-bit number would be the more natural, correct
choice.
Not Breaking Existing Code
To give programmers more control over what size types are, without
having to know about the size of long
or short
on every computer,
the new fixed-size types like int32_t
were added, a few years later.
But now the problem was that some people had already added their own
type named int32_t
to their code, and their code would have stopped
compiling if it was just added to the compiler.
So we have the compiler-specific __int64
type, and know that this is
the name you need to give to get a 64-bit signed integer. They chose
this name, because the C standard says that type names starting with two
underscores are reserved for the compiler maker, so there is no chance
of existing C code using the name __int64
already.
This is fine to old code, from the days where int64_t
didn’t exist.
The only new thing is the __int64
type. Unless the old code’s dev
wrote evil code, nothing has changed, no name can collide and that code
will still compile.
Making the Standard Name Available on Request
But the people who create your compiler are also the ones who write the
<stdint.h>
header file. They can use it as a kind of documentation of
what they did.
If the compiler writers add a typedef __int64 int64_t;
to this
header, that will make sure that the standard name int64_t
will be
translated into __int64
, and so your code can be written to use a
signed 64-bit integer without having to know that the compiler you’re
being compiled by actually named that __int64
under the hood. But this
standard name only exists if your code includes the new header, which
old code won’t do.
This is why you can’t just copy the standard headers to another compiler. The headers must have been written to detect which compiler is compiling them and insert the right compiler-specific types.