C stdint.h Library Reference
Complete reference for stdint.h - fixed-width integers (int8_t, int32_t), limit macros, and portable integer code.
Track Your Progress
Sign in to save your learning progress
What You Will Learn
- ✓Use exact-width integer types
- ✓Choose fastest vs minimum-width types
- ✓Work with pointer-sized integers
- ✓Use format specifier macros
?Why Use stdint.h?
How big is an int? 16 bits? 32 bits? 64 bits? It depends on the platform! This causes bugs in network protocols, file formats, and embedded systems.
Risky
✓Safe
Portable
Same on all platforms
Protocols
Binary formats
Embedded
Hardware registers
01Introduction to stdint.h
# What is stdint.h?
<stdint.h> (C99) provides fixed-width integer typeswith guaranteed sizes. This solves the portability problem where int can be 16, 32, or 64 bits depending on the platform.
Why use fixed-width types?
- Portability: Same size on all platforms
- Clarity: Clear intent about data size
- Binary protocols: Exact sizes for network/file formats
- Hardware programming: Direct register mapping
02Type Categories at a Glance
When to Use Which Type?
Exact-Width
int32_t, uint8_t
✓ Binary file formats
✓ Network protocols
✓ Hardware registers
Fastest
int_fast32_t
✓ CPU-intensive loops
✓ Performance critical
✓ Internal counters
Minimum-Width
int_least32_t
✓ Maximum portability
✓ Always available
✓ Embedded systems
Memory Layout Visualization
uint8_t (1 byte):
uint16_t (2 bytes):
uint32_t (4 bytes):
uint64_t (8 bytes):
Each box represents 8 bits (1 byte)
03Exact-Width Integer Types
What "Exact" Means
int32_t is always exactly 32 bits on every platform. Not "at least 32" — exactly 32. This is critical for binary formats where every byte matters.
These types have exactly the specified width. Optional if the platform doesn't support them (rare on modern systems).
| Signed | Unsigned | Width | Range (Signed) |
|---|---|---|---|
| int8_t | uint8_t | 8 bits | -128 to 127 |
| int16_t | uint16_t | 16 bits | -32,768 to 32,767 |
| int32_t | uint32_t | 32 bits | -2.1B to 2.1B |
| int64_t | uint64_t | 64 bits | ±9.2 quintillion |
1#include <stdio.h>2#include <stdint.h>34int main() {5 // Exact-width types6 int8_t a = 127; // Exactly 8 bits, signed7 uint8_t b = 255; // Exactly 8 bits, unsigned8 int16_t c = 32767; // Exactly 16 bits, signed9 uint16_t d = 65535; // Exactly 16 bits, unsigned10 int32_t e = 2147483647; // Exactly 32 bits, signed11 uint32_t f = 4294967295; // Exactly 32 bits, unsigned12 int64_t g = 9223372036854775807LL; // Exactly 64 bits13 uint64_t h = 18446744073709551615ULL;14 15 printf("int8_t: %d (size: %zu bytes)\n", a, sizeof(a));16 printf("uint8_t: %u (size: %zu bytes)\n", b, sizeof(b));17 printf("int16_t: %d (size: %zu bytes)\n", c, sizeof(c));18 printf("uint16_t: %u (size: %zu bytes)\n", d, sizeof(d));19 printf("int32_t: %d (size: %zu bytes)\n", e, sizeof(e));20 printf("uint32_t: %u (size: %zu bytes)\n", f, sizeof(f));21 printf("int64_t: %lld (size: %zu bytes)\n", (long long)g, sizeof(g));22 printf("uint64_t: %llu (size: %zu bytes)\n", (unsigned long long)h, sizeof(h));23 24 return 0;25}2627/* Output:28int8_t: 127 (size: 1 bytes)29uint8_t: 255 (size: 1 bytes)30int16_t: 32767 (size: 2 bytes)31uint16_t: 65535 (size: 2 bytes)32int32_t: 2147483647 (size: 4 bytes)33uint32_t: 4294967295 (size: 4 bytes)34int64_t: 9223372036854775807 (size: 8 bytes)35uint64_t: 18446744073709551615 (size: 8 bytes)36*/03Minimum-Width Types
These types are at least the specified width. Always available on all platforms.
| Signed | Unsigned | Minimum Width |
|---|---|---|
| int_least8_t | uint_least8_t | At least 8 bits |
| int_least16_t | uint_least16_t | At least 16 bits |
| int_least32_t | uint_least32_t | At least 32 bits |
| int_least64_t | uint_least64_t | At least 64 bits |
1#include <stdio.h>2#include <stdint.h>34int main() {5 // Minimum-width types - guaranteed to exist6 int_least8_t a = 100;7 uint_least16_t b = 50000;8 int_least32_t c = 1000000;9 uint_least64_t d = 10000000000ULL;10 11 printf("int_least8_t: size = %zu bytes\n", sizeof(a));12 printf("uint_least16_t: size = %zu bytes\n", sizeof(b));13 printf("int_least32_t: size = %zu bytes\n", sizeof(c));14 printf("uint_least64_t: size = %zu bytes\n", sizeof(d));15 16 return 0;17}04Fastest Minimum-Width Types
These types are the fastest to process while having at least the specified width. Useful for performance-critical code.
| Signed | Unsigned | Minimum Width |
|---|---|---|
| int_fast8_t | uint_fast8_t | At least 8 bits |
| int_fast16_t | uint_fast16_t | At least 16 bits |
| int_fast32_t | uint_fast32_t | At least 32 bits |
| int_fast64_t | uint_fast64_t | At least 64 bits |
1#include <stdio.h>2#include <stdint.h>34int main() {5 // Fastest types - may be larger than minimum6 printf("Fastest types comparison:\n");7 printf("int_fast8_t: %zu bytes (may be larger for speed)\n", sizeof(int_fast8_t));8 printf("int_fast16_t: %zu bytes\n", sizeof(int_fast16_t));9 printf("int_fast32_t: %zu bytes\n", sizeof(int_fast32_t));10 printf("int_fast64_t: %zu bytes\n", sizeof(int_fast64_t));11 12 // Use case: loop counter13 for (int_fast32_t i = 0; i < 1000000; i++) {14 // Using fastest type for loop counter15 }16 17 return 0;18}1920/* Output (varies by platform):21Fastest types comparison:22int_fast8_t: 4 bytes (may be larger for speed)23int_fast16_t: 4 bytes24int_fast32_t: 4 bytes25int_fast64_t: 8 bytes26*/05Special Types
| Type | Description |
|---|---|
| intptr_t | Signed integer that can hold a pointer value |
| uintptr_t | Unsigned integer that can hold a pointer value |
| intmax_t | Maximum-width signed integer |
| uintmax_t | Maximum-width unsigned integer |
1#include <stdio.h>2#include <stdint.h>34int main() {5 int value = 42;6 int *ptr = &value;7 8 // Convert pointer to integer (for printing, hashing, etc.)9 uintptr_t ptr_as_int = (uintptr_t)ptr;10 printf("Pointer as integer: %lu\n", (unsigned long)ptr_as_int);11 12 // Convert back to pointer13 int *recovered_ptr = (int *)ptr_as_int;14 printf("Value via recovered pointer: %d\n", *recovered_ptr);15 16 // Maximum-width types17 intmax_t big_signed = INTMAX_MAX;18 uintmax_t big_unsigned = UINTMAX_MAX;19 20 printf("\nMaximum types:\n");21 printf("intmax_t size: %zu bytes\n", sizeof(intmax_t));22 printf("uintmax_t size: %zu bytes\n", sizeof(uintmax_t));23 printf("intmax_t max: %jd\n", big_signed);24 printf("uintmax_t max: %ju\n", big_unsigned);25 26 return 0;27}2829/* Output (64-bit):30Pointer as integer: 14073292075133231Value via recovered pointer: 423233Maximum types:34intmax_t size: 8 bytes35uintmax_t size: 8 bytes36intmax_t max: 922337203685477580737uintmax_t max: 1844674407370955161538*/06Limit Macros
| Type | Minimum | Maximum |
|---|---|---|
| int8_t | INT8_MIN | INT8_MAX |
| uint8_t | 0 | UINT8_MAX |
| int16_t | INT16_MIN | INT16_MAX |
| uint16_t | 0 | UINT16_MAX |
| int32_t | INT32_MIN | INT32_MAX |
| uint32_t | 0 | UINT32_MAX |
| int64_t | INT64_MIN | INT64_MAX |
| uint64_t | 0 | UINT64_MAX |
| intmax_t | INTMAX_MIN | INTMAX_MAX |
| uintmax_t | 0 | UINTMAX_MAX |
1#include <stdio.h>2#include <stdint.h>34int main() {5 printf("=== Limit Macros ===\n\n");6 7 printf("8-bit:\n");8 printf(" INT8_MIN: %d\n", INT8_MIN);9 printf(" INT8_MAX: %d\n", INT8_MAX);10 printf(" UINT8_MAX: %u\n", UINT8_MAX);11 12 printf("\n16-bit:\n");13 printf(" INT16_MIN: %d\n", INT16_MIN);14 printf(" INT16_MAX: %d\n", INT16_MAX);15 printf(" UINT16_MAX: %u\n", UINT16_MAX);16 17 printf("\n32-bit:\n");18 printf(" INT32_MIN: %d\n", INT32_MIN);19 printf(" INT32_MAX: %d\n", INT32_MAX);20 printf(" UINT32_MAX: %u\n", UINT32_MAX);21 22 printf("\n64-bit:\n");23 printf(" INT64_MIN: %lld\n", (long long)INT64_MIN);24 printf(" INT64_MAX: %lld\n", (long long)INT64_MAX);25 printf(" UINT64_MAX: %llu\n", (unsigned long long)UINT64_MAX);26 27 return 0;28}2930/* Output:31=== Limit Macros ===32338-bit:34 INT8_MIN: -12835 INT8_MAX: 12736 UINT8_MAX: 255373816-bit:39 INT16_MIN: -3276840 INT16_MAX: 3276741 UINT16_MAX: 65535424332-bit:44 INT32_MIN: -214748364845 INT32_MAX: 214748364746 UINT32_MAX: 4294967295474864-bit:49 INT64_MIN: -922337203685477580850 INT64_MAX: 922337203685477580751 UINT64_MAX: 1844674407370955161552*/07Format Specifier Macros
<inttypes.h> (included with stdint.h) provides portable format specifier macros for printf/scanf.
1#include <stdio.h>2#include <stdint.h>3#include <inttypes.h> // For format macros45int main() {6 int8_t a = 42;7 uint16_t b = 65000;8 int32_t c = -123456;9 uint64_t d = 9876543210ULL;10 11 // Use PRId, PRIu, PRIx macros for portable printing12 printf("int8_t: %" PRId8 "\n", a); // PRId8 = "hhd" or similar13 printf("uint16_t: %" PRIu16 "\n", b); // PRIu1614 printf("int32_t: %" PRId32 "\n", c); // PRId3215 printf("uint64_t: %" PRIu64 "\n", d); // PRIu6416 17 // Hex format18 printf("\nHex format:\n");19 printf("uint16_t: 0x%" PRIX16 "\n", b); // PRIX16 = uppercase hex20 printf("uint64_t: 0x%" PRIx64 "\n", d); // PRIx64 = lowercase hex21 22 // Scanf macros23 int32_t input;24 printf("\nEnter a 32-bit integer: ");25 // scanf("%" SCNd32, &input); // Use SCNd32 for scanf26 27 // Common macros:28 // PRId8, PRId16, PRId32, PRId64 - signed decimal29 // PRIu8, PRIu16, PRIu32, PRIu64 - unsigned decimal30 // PRIx8, PRIx16, PRIx32, PRIx64 - lowercase hex31 // PRIX8, PRIX16, PRIX32, PRIX64 - uppercase hex32 // SCNd8, SCNd16, SCNd32, SCNd64 - scanf signed33 // SCNu8, SCNu16, SCNu32, SCNu64 - scanf unsigned34 35 return 0;36}3738/* Output:39int8_t: 4240uint16_t: 6500041int32_t: -12345642uint64_t: 98765432104344Hex format:45uint16_t: 0xFDE846uint64_t: 0x24cb016ea47*/08Practical Example: Binary File Format
1#include <stdio.h>2#include <stdint.h>3#include <string.h>45// Binary file header with exact-size fields6struct FileHeader {7 uint32_t magic; // 4 bytes: Magic number8 uint16_t version; // 2 bytes: File version9 uint16_t flags; // 2 bytes: Flags10 uint32_t data_offset; // 4 bytes: Offset to data11 uint32_t data_size; // 4 bytes: Size of data12 // Total: 16 bytes (no padding needed)13};1415#define MAGIC_NUMBER 0x46494C45 // "FILE" in hex1617void write_file(const char *filename, const char *data) {18 FILE *fp = fopen(filename, "wb");19 if (!fp) return;20 21 struct FileHeader header = {22 .magic = MAGIC_NUMBER,23 .version = 1,24 .flags = 0,25 .data_offset = sizeof(struct FileHeader),26 .data_size = strlen(data)27 };28 29 fwrite(&header, sizeof(header), 1, fp);30 fwrite(data, strlen(data), 1, fp);31 32 fclose(fp);33 printf("File written successfully\n");34}3536void read_file(const char *filename) {37 FILE *fp = fopen(filename, "rb");38 if (!fp) return;39 40 struct FileHeader header;41 fread(&header, sizeof(header), 1, fp);42 43 printf("Magic: 0x%08X\n", header.magic);44 printf("Version: %u\n", header.version);45 printf("Data offset: %u bytes\n", header.data_offset);46 printf("Data size: %u bytes\n", header.data_size);47 48 // Read data49 char buffer[256] = {0};50 fseek(fp, header.data_offset, SEEK_SET);51 fread(buffer, header.data_size, 1, fp);52 printf("Data: %s\n", buffer);53 54 fclose(fp);55}5657int main() {58 printf("Header size: %zu bytes\n\n", sizeof(struct FileHeader));59 60 write_file("test.bin", "Hello, fixed-width types!");61 printf("\n");62 read_file("test.bin");63 64 return 0;65}6667/* Output:68Header size: 16 bytes6970File written successfully7172Magic: 0x46494C4573Version: 174Data offset: 16 bytes75Data size: 26 bytes76Data: Hello, fixed-width types!77*/!Code Pitfalls: Common Mistakes & What to Watch For
Copied code often uses %d or %lld for fixed-width types like int32_t and int64_t, but the correct format specifiers vary by platform.
If you search online to "print a 64-bit integer," it might use printf("%lld", value). But int64_t isn't always long long — on some 64-bit systems, it's long. The correct portable way is printf("%" PRId64, value) using the macros from inttypes.h.
The Trap: Online sources often use %lld for all 64-bit types, which is correct on Windows but may produce warnings or wrong output on Linux. They also might forget that scanf() needs SCNd32/SCNd64 instead of the PRId macros.
The Reality: Always use <inttypes.h> format macros: PRId32, PRIu64, PRIx16, SCNd64, etc. This ensures portability across platforms. The macros expand to the correct format for each system.
10Frequently Asked Questions
Q:When should I use int32_t instead of int?
A: Use fixed-width types when you need a specific size — binary file formats, network protocols, hardware registers, or cross-platform code. Use int for general computations where exact size doesn't matter.
Q:What's the difference between int_least16_t and int_fast16_t?
A: int_least16_t is the smallest type with at least 16 bits (saves memory).int_fast16_t is the fastest type with at least 16 bits (may be 32 or 64 bits for speed).
Q:Why do I need PRId32 instead of just %d?
A: int32_t isn't always the same as int! On some platforms, int32_t is long. PRId32 expands to the correct format string for your platform, ensuring portable code.
Q:How do I convert a pointer to an integer?
A: Useuintptr_t:(uintptr_t)ptr. It's guaranteed to be large enough to hold any pointer value on your platform. Cast back with (void*)int_val.
10Quick Reference
Exact-Width (use for protocols)
int8_t, int16_t, int32_t, int64_t
uint8_t, uint16_t, uint32_t, uint64_t
Fastest (use for performance)
int_fast8_t, int_fast16_t, ...
uint_fast8_t, uint_fast16_t, ...
Special
intptr_t, uintptr_t (pointer size)
intmax_t, uintmax_t (maximum)
Format Macros
PRId32, PRIu64, PRIx16 (printf)
SCNd32, SCNu64 (scanf)
assert.h Library
Back to →All Tutorials
Test Your Knowledge
Related Tutorials
C assert.h Library Reference
Complete reference for assert.h - runtime assertions, NDEBUG, static_assert (C11), and debugging best practices.
C errno.h Library Reference
Complete reference for errno.h - error handling with errno, perror(), strerror(), and common error codes.
C stddef.h Library Reference
Complete reference for stddef.h - NULL, size_t, ptrdiff_t, offsetof, and fundamental type definitions.
Have Feedback?
Found something missing or have ideas to improve this tutorial? Let us know on GitHub!