C stdint.h Header

Fixed-width integer types and their limits

🎯 What is stdint.h?

The stdint.h header provides exact-width integer types like int32_t and uint64_t, ensuring consistent behavior across different platforms and architectures in your C programs.


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

int main() {
    int32_t exact32 = 42;
    uint64_t exact64 = 1000ULL;
    printf("32-bit: %d, 64-bit: %llu\n", exact32, exact64);
    return 0;
}
                                    

Output:

32-bit: 42, 64-bit: 1000

Key stdint.h Types

🎯

Exact Width

Types with exact number of bits

int8_t, int16_t, int32_t
uint8_t, uint16_t, uint32_t
📏

Minimum Width

Types with at least N bits

int_least8_t, int_least16_t
uint_least8_t, uint_least16_t

Fastest Types

Fastest types with at least N bits

int_fast8_t, int_fast16_t
uint_fast8_t, uint_fast16_t
🔢

Maximum Types

Largest available integer types

intmax_t, uintmax_t
intptr_t, uintptr_t

🔹 Exact-Width Integer Types

C provides exact-width integer types that guarantee specific bit widths across different platforms and compilers. Types like int8_t, int16_t, int32_t, and int64_t from <stdint.h> ensure your integers have exactly 8, 16, 32, or 64 bits respectively, regardless of the target architecture. This is crucial for portable code, especially in embedded systems, network protocols, and binary file formats where precise data sizes matter. For example, uint32_t counter = 0; guarantees a 32-bit unsigned integer, preventing unexpected overflow behavior or compatibility issues when moving code between 32-bit and 64-bit systems.

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

int main() {
    // Exact width types
    int8_t  byte_val = 127;        // Exactly 8 bits
    int16_t short_val = 32767;     // Exactly 16 bits  
    int32_t int_val = 2147483647;  // Exactly 32 bits
    int64_t long_val = 9223372036854775807LL; // Exactly 64 bits
    
    printf("8-bit: %d\n", byte_val);
    printf("16-bit: %d\n", short_val);
    printf("32-bit: %d\n", int_val);
    printf("64-bit: %lld\n", long_val);
    
    // Unsigned versions
    uint8_t ubyte = 255;
    uint32_t uint_val = 4294967295U;
    
    printf("Unsigned 8-bit: %u\n", ubyte);
    printf("Unsigned 32-bit: %u\n", uint_val);
    
    return 0;
}

Output:

8-bit: 127
16-bit: 32767
32-bit: 2147483647
64-bit: 9223372036854775807
Unsigned 8-bit: 255
Unsigned 32-bit: 4294967295

🔹 Minimum and Fast Types

Minimum-width and fast-width integer types offer flexibility for performance optimization while maintaining portability. The int_least types like int_least16_t guarantee at least the specified number of bits, while int_fast types like int_fast32_t provide the fastest integer type with at least the specified width on the current platform. Using int_fast16_t for loop counters or temporary calculations can improve performance because the compiler selects the most efficient type for your processor architecture. These types balance portability with performance, allowing the compiler to optimize for speed when exact bit widths aren't critical to your algorithm.

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

int main() {
    // Minimum width types (at least N bits)
    int_least8_t  min8 = 100;
    int_least16_t min16 = 30000;
    
    // Fastest types (fastest with at least N bits)
    int_fast8_t  fast8 = 50;
    int_fast32_t fast32 = 1000000;
    
    printf("Least 8-bit: %d\n", min8);
    printf("Least 16-bit: %d\n", min16);
    printf("Fast 8-bit: %d\n", fast8);
    printf("Fast 32-bit: %d\n", fast32);
    
    // Show actual sizes
    printf("\nActual sizes:\n");
    printf("int_least8_t: %zu bytes\n", sizeof(int_least8_t));
    printf("int_fast8_t: %zu bytes\n", sizeof(int_fast8_t));
    
    return 0;
}

Output:

Least 8-bit: 100
Least 16-bit: 30000
Fast 8-bit: 50
Fast 32-bit: 1000000

Actual sizes:
int_least8_t: 1 bytes
int_fast8_t: 1 bytes

🔹 Maximum and Pointer Types

Maximum-width and pointer-sized integer types handle special cases requiring the largest available integers or pointer arithmetic. The intmax_t and uintmax_t types represent the largest integer types supported by your implementation, useful for calculations that need maximum range. Meanwhile, intptr_t and uintptr_t are guaranteed to hold any valid pointer value, making them essential for low-level pointer manipulation and type-punning operations. For example, uintptr_t addr = (uintptr_t)ptr; safely converts a pointer to an integer for bit manipulation or address calculations, which is particularly important in memory allocators, garbage collectors, and systems programming.

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

int main() {
    // Maximum width types
    intmax_t max_signed = INTMAX_MAX;
    uintmax_t max_unsigned = UINTMAX_MAX;
    
    printf("Max signed: %jd\n", max_signed);
    printf("Max unsigned: %ju\n", max_unsigned);
    
    // Pointer-sized integers
    int value = 42;
    intptr_t ptr_as_int = (intptr_t)&value
    uintptr_t ptr_as_uint = (uintptr_t)&value
    
    printf("Pointer as signed: %jd\n", (intmax_t)ptr_as_int);
    printf("Pointer as unsigned: %ju\n", (uintmax_t)ptr_as_uint);
    
    // Convert back to pointer
    int *restored_ptr = (int*)ptr_as_int;
    printf("Original value: %d\n", *restored_ptr);
    
    return 0;
}

Output:

Max signed: 9223372036854775807
Max unsigned: 18446744073709551615
Pointer as signed: 140732920755068
Pointer as unsigned: 140732920755068
Original value: 42

🧠 Test Your Knowledge

Which type guarantees exactly 32 bits?