C Integer Types and Bit Widths by Architecture
In the C programming language, the exact bit width of basic integer
types is not strictly fixed by the language standard; instead, it varies
based on target processor architecture, operating system design, and the
compiler's Application Binary Interface (ABI). To balance hardware
efficiency with software portability, the ISO C standard dictates only
minimum value ranges and relative size relationships rather than
explicit bit lengths. As a result, identical C code can allocate
differing numbers of bits for types like int and
long depending on whether it runs on an 8-bit
microcontroller, a 32-bit processor, or a 64-bit desktop CPU.
The C Standard Guarantees
Rather than specifying exact bit widths, the C standard guarantees relative size constraints:
sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long)
It also specifies minimum storage capacities for each type:
char: At least 8 bits (defined as 1 byte in C terms).short: At least 16 bits.int: At least 16 bits.long: At least 32 bits.long long: At least 64 bits (introduced in C99).
Because these are minimum requirements, a compiler targeting a specific platform is free to expand any type to align with the processor's natural register width or memory bus.
Data Models Across Architectures
Hardware architectures generally determine the word size—the native amount of data a CPU processes in a single register operation. Operating systems configure compilers to use specific "data models" to define how standard types map onto these register widths.
16-Bit Architectures (e.g., MS-DOS, Embedded Microcontrollers)
Historically common in older PCs and still present in low-power microcontrollers (such as 16-bit PIC or MSP430):
- Model: Often referred to as IP16 or LP32.
int: 16 bits.long: 32 bits.- Pointer: 16 bits (or segment-offset pairs up to 32 bits).
32-Bit Architectures (e.g., x86, ARM32)
Common in 32-bit desktop platforms, mobile devices, and embedded systems:
- Model: ILP32 (Integer, Long, and Pointer are 32-bit).
short: 16 bits.int: 32 bits.long: 32 bits.long long: 64 bits.- Pointer: 32 bits.
64-Bit Architectures (e.g., x86-64, AArch64)
In modern 64-bit systems, the implementation splits significantly between Unix-like environments and Microsoft Windows.
- LP64 (Linux, macOS, BSD, Solaris):
int: 32 bits.long: 64 bits.long long: 64 bits.- Pointer: 64 bits.
- In LP64,
longscales to the native 64-bit register size, makingsizeof(long) == sizeof(void*).
- LLP64 (Microsoft Windows 64-bit):
int: 32 bits.long: 32 bits.long long: 64 bits.- Pointer: 64 bits.
- Microsoft preserved 32-bit
longtypes to maintain backward compatibility with legacy 32-bit Windows codebases, requiring developers to uselong longfor native 64-bit integers.
Summary Comparison Table
| Data Model | Target Systems | short |
int |
long |
long long |
Pointer |
|---|---|---|---|---|---|---|
| LP32 | 16-bit DOS / Microcontrollers | 16-bit | 16-bit | 32-bit | 64-bit | 16-bit |
| ILP32 | 32-bit Unix, Windows 32 | 16-bit | 32-bit | 32-bit | 64-bit | 32-bit |
| LLP64 | 64-bit Windows | 16-bit | 32-bit | 32-bit | 64-bit | 64-bit |
| LP64 | 64-bit Linux, macOS, Unix | 16-bit | 32-bit | 64-bit | 64-bit | 64-bit |
Achieving Fixed Bit
Widths with <stdint.h>
Because relying on int or long introduces
portability issues across architectures, the C99 revision introduced the
<stdint.h> header. When an application requires
unambiguous, cross-platform sizes—such as in binary serialization,
network protocols, or hardware register manipulation—standard
fixed-width types should be used:
int8_t/uint8_t: Exactly 8 bits.int16_t/uint16_t: Exactly 16 bits.int32_t/uint32_t: Exactly 32 bits.int64_t/uint64_t: Exactly 64 bits.intptr_t/uintptr_t: An integer type guaranteed to be the exact same width as a pointer on that platform.