Fixed-Width Integers in C: The stdint.h Header

In C programming, traditional integer types such as int or long can vary in size across different hardware platforms and compilers, often leading to portability bugs. To solve this problem, the C99 standard introduced fixed-width integer types like int32_t and uint64_t, which guarantee exact bit widths across all compliant platforms. This article covers the specific header file that defines these types, explains the available variations, and demonstrates how to implement them in your code.

The Standard Header: <stdint.h>

The standard header that provides fixed-width integer types in the C language is <stdint.h>. When programming in C++, the equivalent header is <cstdint>, which places the same types within the std namespace as well as the global namespace.

Including this header allows developers to declare integer variables with explicit memory footprints, ensuring cross-platform predictability for binary data processing, memory-mapped I/O, network protocols, and cryptographic algorithms.

#include <stdint.h>

Categories of Fixed-Width Integer Types

The <stdint.h> header categorizes integer definitions into several distinct groups depending on performance and architectural requirements:

1. Exact-Width Integer Types

These types guarantee exact sizes without any padding bits:

  • Signed: int8_t, int16_t, int32_t, int64_t
  • Unsigned: uint8_t, uint16_t, uint32_t, uint64_t

If a specific architecture cannot natively support an exact width (for example, a system with 32-bit minimum addressable units cannot define int8_t), the corresponding type will not be provided by that implementation.

2. Minimum-Width Integer Types

These guarantee at least the specified number of bits, but the implementation may choose a larger type if it is more suitable:

  • Signed: int_least8_t, int_least16_t, int_least32_t, int_least64_t
  • Unsigned: uint_least8_t, uint_least16_t, uint_least32_t, uint_least64_t

3. Fastest Minimum-Width Integer Types

These represent the fastest integer types on the target hardware that have at least the designated width:

  • Signed: int_fast8_t, int_fast16_t, int_fast32_t, int_fast64_t
  • Unsigned: uint_fast8_t, uint_fast16_t, uint_fast32_t, uint_fast64_t

4. Pointer-Holding Integers

These types are guaranteed to be wide enough to store a void pointer safely:

  • intptr_t (signed)
  • uintptr_t (unsigned)

5. Maximum-Width Integers

These define the largest supported integer widths on the system:

  • intmax_t (signed)
  • uintmax_t (unsigned)

Practical Implementation Example

#include <stdio.h>
#include <stdint.h>

int main(void) {
    int32_t signed_integer = -2147483648;
    uint64_t unsigned_large_integer = 18446744073709551615ULL;

    printf("Size of int32_t: %zu bytes\n", sizeof(signed_integer));
    printf("Size of uint64_t: %zu bytes\n", sizeof(unsigned_large_integer));

    return 0;
}

Formatted Printing with <inttypes.h>

Because the underlying fundamental type for fixed-width integers varies (e.g., int64_t might be defined as long on 64-bit Linux, but long long on 64-bit Windows), standard format specifiers like %d or %lld can trigger compiler warnings.

To print these types portably, include <inttypes.h>, which wraps <stdint.h> and provides standard format macros:

#include <stdio.h>
#include <inttypes.h>

int main(void) {
    int32_t count = 42;
    uint64_t big_number = 10000000000ULL;

    printf("Count: %" PRId32 "\n", count);
    printf("Big Number: %" PRIu64 "\n", big_number);

    return 0;
}