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.
Fundamentals
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.
Foundation
📝 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
Signed integer: 42
Unsigned integer: 65535
sizeof(int32_t)=4, sizeof(uint16_t)=2
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.
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;
}
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.
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.
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.
Applications
🚀 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.
Important
📝 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.
Performance
⚡ 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.
Wrap Up
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.
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
Summary
Key Takeaways
Knowledge Unlocked
Five things to remember about stdint.h
Portable integer sizes, explained simply.
5
Core concepts
💬01
int32_t
Exactly 32 bits.
Exact
📚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.