The standard modern complaint about C — that platform-dependent integer sizes were a design blunder responsible for decades of portability bugs — judges a 1970s decision by 2020s conditions. When C was designed, word sizes ran 12, 16, 18, 24, 32, 36, 48, 60 and 64 bits, characters were 6, 7, 8 or 9 bits, negative numbers came in three representations, and some machines could not address a byte at all. int meant “the natural integer of the machine” — C11 §6.2.5 still says exactly that.
- C’s types came from BCPL and B, which were typeless: one value kind, the machine word. Bell Labs’ byte-addressed PDP-11 with floating-point hardware broke that, so
charbecame the byte andintkept the spirit of the word - By 1978 it already worked. K&R 1st edition lists C running on a PDP-11 (16-bit int), IBM 370 (32-bit), Interdata 8/32 (32-bit) and Honeywell 6000 — 36-bit int, 9-bit char
- Fixed 32-bit
intwould have cost real instructions: on a PDP-11 every int op becomesADD+ADC, doubling the cost of every loop counter and array index; on a 36-bit machine the compiler would mask after nearly every arithmetic op to preserve exact wraparound - Ones’-complement machines (UNIVAC 1100, CDC 6600) explain why signed overflow is undefined behavior — it began as a way to avoid forcing one machine’s overflow semantics onto every other machine, not as a gift to optimizers
- ANSI C codified minimums, not exact sizes:
char8,short/int16,long32,long long64. You ask what the smallest type guaranteed to hold your range is - Lua 5.4 is the working exemplar:
LUAI_IS32INT ((UINT_MAX >> 30) >= 3)checks whetherunsigned intholds at least 32 bits;lu_byteisunsigned charbecauseuint8_tmay not exist; binary packing shifts byCHAR_BITinstead of assuming 8 - The costs were real and the author says so: code assuming
sizeof(int) == sizeof(void *)broke moving to 32- then 64-bit; LP64 vs LLP64 madelongmean different things on Unix and 64-bit Windows;<stdint.h>only arrived in C99 - Even
<stdint.h>kept the philosophy — exact-width types are optional,int_least32_tandint_fast32_tare mandatory. DSPs with 16- or 32-bitCHAR_BITare still in production
The most useful reframing here is undefined signed overflow as a portability device. That one rule explains a lot of C that otherwise looks arbitrary.
And the idea outlived the era it was designed for. Rust’s isize, Go’s int, Swift’s Int and Zig’s usize are all platform-sized — they narrowed the concept from “the machine’s natural word” to “the size of a pointer,” because indexing memory is the one place word size still matters. C23 finally mandates two’s complement, but only because the machines that justified the old rule are nearly gone: the standard followed reality after reality settled.