C Standard Library stdint.h

Beginner
⏱️ 12 min read
📚 Updated: Jul 2026
🎯 5 Examples
<stdint.h>

What You’ll Learn

On one machine int might be 32 bits; on another it could differ. <stdint.h> (C99) gives you names like int32_t and uint8_t with guaranteed widths—essential for file formats, network protocols, and hardware registers where every byte must match a specification.

01

int32_t

Exact 32-bit.

02

uint8_t

Byte / octet.

03

int_least

At least N bits.

04

int_fast

Fastest N+ bits.

05

INT32_MAX

Limit macros.

06

PRI macros

Safe printf.

Definition and Usage

stdint.h typedefs signed and unsigned integers with explicit bit counts. An int32_t is always exactly 32 bits (when the type exists on the platform). A uint8_t is always an 8-bit unsigned byte.

The header also defines least-width types (smallest type with at least N bits), fast-width types (fastest type with at least N bits), pointer-sized integers (intptr_t), maximum-width integers (intmax_t), and limit macros such as INT32_MAX and UINT8_MAX.

💡
Beginner Tip

For everyday loops and array indices, plain int is still fine. Reach for stdint.h when the size in bits is part of your design—binary files, CRCs, pixel buffers, or cross-platform APIs.

📝 Syntax

Include the header:

C
#include <stdint.h>

Exact-width integer types (most common)

  • int8_t, uint8_t — 8-bit signed / unsigned
  • int16_t, uint16_t — 16-bit signed / unsigned
  • int32_t, uint32_t — 32-bit signed / unsigned
  • int64_t, uint64_t — 64-bit signed / unsigned (if available)

Other typedef families

  • int_least16_t, uint_least16_t — at least 16 bits, smallest such type
  • int_fast16_t, uint_fast16_t — at least 16 bits, fastest such type
  • intptr_t, uintptr_t — hold a pointer value as an integer
  • intmax_t, uintmax_t — widest integer types available

Limit macros (examples)

C
INT8_MIN,   INT8_MAX,   UINT8_MAX
INT16_MIN,  INT16_MAX,  UINT16_MAX
INT32_MIN,  INT32_MAX,  UINT32_MAX
INT64_MIN,  INT64_MAX,  UINT64_MAX

Printing with inttypes.h (recommended)

C
#include <inttypes.h>   /* includes stdint.h */

int32_t  x = -42;
uint16_t y = 65535;

printf("x = %" PRId32 "\n", x);
printf("y = %" PRIu16 "\n", y);

Headers and linking

  • #include <stdint.h> — typedefs and limit macros only; no link flag.
  • #include <inttypes.h> — adds PRI* and SCN* macros for printf/scanf.
  • C99 or later: gcc program.c -std=c11 -o program.

⚡ Quick Reference

TypeWidthTypical use
uint8_t8 bitsRaw bytes, RGB channels
int16_t16 bitsAudio samples, small counters
uint32_t32 bitsIPv4 addresses, timestamps
int64_t64 bitsLarge counters, file sizes
intptr_tpointer-sizedHashing pointers, low-level tricks
INT32_MAXmacroRange checks before overflow
Declare
uint32_t id = 0;

Fixed width

Print
% PRId32

inttypes.h

Max
UINT16_MAX

65535

Bytes
uint8_t buf[4];

Octet array

Examples Gallery

Compile with gcc file.c -std=c11 -o out. Examples 1 and 2 use portable PRI macros from <inttypes.h> instead of plain %d/%u.

📚 Getting Started

Basic fixed-width variables from the reference, with correct printing.

Example 1 — int32_t and uint16_t

From the reference: declare exact-width variables and print them portably.

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

int main(void) {
    int32_t  my_int = 42;
    uint16_t my_unsigned = 65535;

    printf("Signed integer:   %" PRId32 "\n", my_int);
    printf("Unsigned integer: %" PRIu16 "\n", my_unsigned);
    printf("sizeof(int32_t)=%zu, sizeof(uint16_t)=%zu\n",
           sizeof(my_int), sizeof(my_unsigned));

    return 0;
}

How It Works

int32_t is always 4 bytes; uint16_t is always 2 bytes when those types exist. 65535 is UINT16_MAX. The old reference used %d and %u—that often works on common platforms but PRId32/PRIu16 are the portable choice.

Example 2 — uint8_t Byte Buffer (RGB Pixel)

Three uint8_t values store one pixel’s red, green, and blue channels.

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

typedef struct {
    uint8_t r;
    uint8_t g;
    uint8_t b;
} Pixel;

int main(void) {
    Pixel orange = { 255, 165, 0 };

    printf("RGB pixel: (%u, %u, %u)\n",
           orange.r, orange.g, orange.b);
    printf("Pixel size: %zu bytes\n", sizeof(Pixel));

    return 0;
}

How It Works

uint8_t holds values 0–255 (UINT8_MAX). Each channel is exactly one byte, which matches how image formats describe raw pixel data. Struct padding might add bytes on some platforms—for wire formats you may need #pragma pack or careful ordering (advanced topic).

📈 Practical Patterns

Limits, printing, and binary layout.

Example 3 — Checking Range with INT32_MAX

Use limit macros from stdint.h instead of magic numbers.

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

bool fits_int32(long value) {
    return value >= INT32_MIN && value <= INT32_MAX;
}

int main(void) {
    long candidates[] = { -2147483648L, 0L, 2147483647L, 3000000000L };
    size_t i;

    printf("INT32_MIN = %ld\n", (long)INT32_MIN);
    printf("INT32_MAX = %ld\n", (long)INT32_MAX);

    for (i = 0; i < 4; i++) {
        printf("%ld -> %s\n", candidates[i],
               fits_int32(candidates[i]) ? "fits" : "overflow");
    }

    return 0;
}

How It Works

INT32_MAX is 2,147,483,647. The value 3,000,000,000 does not fit in a signed 32-bit integer. Always validate before casting or serializing to a smaller fixed-width type.

Example 4 — Printing uint64_t with PRIu64

64-bit values need the matching PRI macro—not %lu on every platform.

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

int main(void) {
    uint64_t bytes = 5000000000ULL;

    printf("File size: %" PRIu64 " bytes\n", bytes);
    printf("In hex:    0x%" PRIx64 "\n", bytes);

    return 0;
}

How It Works

PRIu64 expands to the correct length modifier for uint64_t on your compiler (often lu or llu). The pattern is "%" PRIu64—the macro concatenates with the percent sign.

Example 5 — Portable File Header with uint32_t

Fixed-width fields make binary headers predictable across operating systems.

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

typedef struct {
    uint32_t magic;      /* 'CFUN' as number */
    uint16_t version;
    uint16_t flags;
    uint32_t payload_size;
} FileHeader;

int main(void) {
    FileHeader hdr = {
        .magic = 0x4346554Eu,  /* CFUN in ASCII hex */
        .version = 1,
        .flags = 0,
        .payload_size = 1024
    };

    printf("magic=0x%08" PRIx32 ", version=%" PRIu16
           ", payload=%" PRIu32 " bytes\n",
           hdr.magic, hdr.version, hdr.payload_size);
    printf("Header size: %zu bytes\n", sizeof(FileHeader));

    return 0;
}

How It Works

Each field has a known width: 4 + 2 + 2 + 4 = 12 bytes when the compiler does not insert padding between members. Real on-disk formats also specify byte order (endianness)—that is a separate topic beyond this intro.

🚀 Common Use Cases

  • Binary file formats — headers and records with fixed field sizes.
  • Network protocols — TCP/IP fields, packet lengths, port numbers.
  • Embedded systems — hardware registers mapped to uint32_t.
  • Graphics and audio — pixels (uint8_t), samples (int16_t).
  • Cross-platform libraries — public APIs that must behave identically everywhere.
  • Cryptography and hashes — byte arrays as uint8_t buffers.

🧠 How stdint.h Works

1

Platform has base types

Compiler provides char, short, int, long long, etc.

Compiler
2

stdint.h maps widths

typedef picks the right base type for each int32_t-style name.

Typedefs
3

Your code uses names

Same source compiles with the same bit layout intent on other targets.

Portability
=

Predictable integers

No guessing whether int is 16 or 32 bits on a new machine.

📝 Notes

  • C99 feature—compile with -std=c99 or newer.
  • Exact-width types exist only if the implementation supports that width.
  • Use <inttypes.h> for printf/scanf format macros (PRId32, SCNd32, etc.).
  • Fixed-width structs may still have padding—stdint fixes member widths, not overall struct layout.
  • Endianness (byte order) is not handled by stdint.h—serialize explicitly for network/disk.
  • int is still appropriate for most application logic; do not replace every int with int32_t without reason.

⚡ Optimization

int_fast16_t may be faster than int16_t on some CPUs because the CPU prefers native word size. For hot loops, profiling can justify fast types. For on-wire layout, always use exact-width types (uint16_t), not fast types. Typedefs have zero runtime overhead—they are aliases checked at compile time.

Conclusion

<stdint.h> gives C programmers a portable vocabulary for integer sizes. Use int32_t, uint8_t, and friends when bits matter; use INT32_MAX for ranges; use PRI macros from inttypes.h when printing.

Together with <stddef.h> for sizes and pointers, stdint.h is a foundation for systems programming and any code that crosses platform boundaries.

💡 Best Practices

✅ Do

  • Use exact-width types in binary protocols and file formats
  • Print with PRId32, PRIu64, etc.
  • Check ranges with INT32_MIN / INT32_MAX
  • Prefer uint8_t for raw byte buffers
  • Include <stdint.h> in public API headers when sizes matter

❌ Don’t

  • Assume %d matches int32_t on every platform
  • Replace all int with int32_t unnecessarily
  • Ignore struct padding in wire-format structs
  • Assume int64_t exists without checking the target
  • Mix endianness when reading binary data from disk or network

Key Takeaways

Knowledge Unlocked

Five things to remember about stdint.h

Portable integer sizes, explained simply.

5
Core concepts
📚 02

uint8_t

One byte.

Byte
📈 03

INT32_MAX

Range limit.

Macro
📄 04

PRIu64

Printf safe.

Print
🌐 05

C99

Portable.

Standard

❓ Frequently Asked Questions

stdint.h is a C99 header that typedefs integers with explicit bit widths—int8_t, uint16_t, int32_t, uint64_t, and related least-width and fast-width types. It lets you write portable code when exact sizes matter, such as file formats, network packets, and embedded hardware registers.
Use int32_t when the value must be exactly 32 bits on every platform—for example a file header field or network protocol. Use int for everyday loop counters and array indices where the platform's natural int is fine. int size varies; int32_t does not.
int_leastN_t is the smallest signed type with at least N bits. int_fastN_t is the fastest signed type with at least N bits on that implementation. Exact-width types (int16_t) are exactly N bits when available; least and fast types trade exact size for availability or speed.
Include inttypes.h (which pulls in stdint.h) and use the PRI macros: printf("%" PRId32, value) for int32_t and printf("%" PRIu64, value) for uint64_t. Plain %d or %lu may mismatch the actual type on some platforms.
Not guaranteed. The standard only requires int64_t if the implementation provides a 64-bit integer type. Most modern desktop compilers do; some small embedded targets may not. Check INT64_MAX in stdint.h or use intmax_t when you need the widest available type.
limits.h documents the ranges of built-in types like int and long (CHAR_BIT, INT_MAX, etc.). stdint.h defines new typedef names with chosen widths and provides matching limit macros like INT32_MAX and UINT8_MAX for those typedefs. Use both when writing portable low-level code.
Did you know?

Before C99, programmers used typedefs like typedef unsigned char BYTE in every project. stdint.h standardized those names so libraries could share headers without colliding on custom type names.

Explore C Standard Library Headers

Continue with stdio.h or browse the library index.

Standard Library Index →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

5 people found this page helpful