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.