The int type in C is one of the oldest anti-patterns still in active use. It feels natural — it’s short, it’s everywhere, and it “just works.” Until it doesn’t.
The Problem: int Has No Fixed Size
The C standard guarantees only that int is at least 16 bits. That’s it. The actual size depends on the compiler and target architecture:
| Platform | sizeof(int) |
|---|---|
| Arduino (AVR) | 2 bytes (16-bit) |
| x86 Linux/Windows | 4 bytes (32-bit) |
| Most 64-bit systems | 4 bytes (32-bit) |
| Some embedded systems | 2 bytes (16-bit) |
This means code that “works” on your machine can silently break when compiled elsewhere.
Real-World Disasters
The $370 Million Explosion: Ariane 5 Flight 501
On June 4, 1996, the European Space Agency launched the Ariane 5 rocket on its maiden flight. Thirty-seven seconds after liftoff, the rocket veered off course and self-destructed. Total loss: $370 million in hardware and four scientific satellites.
The cause? A 64-bit floating-point number was converted to a 16-bit signed integer. The value was larger than 32,767 (the maximum for a signed 16-bit int), causing an overflow. The navigation system interpreted the garbage value as a legitimate flight correction, sending the rocket tumbling.
-- Simplified reconstruction of the fatal conversion
-- The horizontal velocity value exceeded 16-bit range
L_M_BV_32 := TBD.T_ENTIER_32S((1.0/C_M_LSB_BV) * G_M_INFO_DERIVE(T_ALG.E_BV));
-- This was then converted to 16-bit, causing overflow
BV := TDB.T_ENTIER_16S(L_M_BV_32); -- BOOMThe tragic irony: this code was reused from Ariane 4, where the values never exceeded 16-bit range. Nobody verified the assumptions still held for the faster Ariane 5.
The official investigation report concluded: “The internal SRI software exception was caused during execution of a data conversion from 64-bit floating point to 16-bit signed integer value.”
This is the most expensive integer overflow bug in history — and a permanent reminder of why fixed-width types with explicit range checking matter.
Silent Overflow
// Works fine on 32-bit int systems
int sensor_reading = 100000;
// Silently wraps on 16-bit int systems
// 100000 becomes 34464 (100000 % 65536)No warning. No error. Just wrong data.
Security Vulnerabilities
Integer overflow is a classic attack vector. When int size is unpredictable, so is overflow behavior:
int length = get_user_input(); // Attacker controls this
char *buffer = malloc(length); // What if length overflowed?
memcpy(buffer, data, length); // Buffer overflowThe 2014 Heartbleed vulnerability was partly caused by integer size assumptions. Countless CVEs trace back to int overflow on unexpected platforms.
Cross-Platform Data Corruption
Serializing an int and deserializing on another platform:
// Machine A (32-bit int): writes 4 bytes
fwrite(&value, sizeof(int), 1, file);
// Machine B (16-bit int): reads 2 bytes
fread(&value, sizeof(int), 1, file);
// Reads garbage, file position now wrongBinary protocols, file formats, network packets — all break silently.
The Fix: Fixed-Width Integer Types
C99 introduced <stdint.h> with types that guarantee their size:
#include <stdint.h>
int32_t counter; // Always 32 bits, signed
uint32_t flags; // Always 32 bits, unsigned
int16_t sensor_value; // Always 16 bits, signed
uint8_t byte; // Always 8 bits, unsigned
int64_t timestamp; // Always 64 bits, signedWhy Fixed-Width Types Are Better
- Portable — Same size on every platform
- Self-documenting —
int32_ttells you exactly what to expect - Predictable overflow — You know when it will happen
- Binary compatibility — Safe for serialization and protocols
- Security — Easier to audit for overflow vulnerabilities
When int Is Still Acceptable
There are a few narrow cases:
- Loop counters for small, bounded iterations:
for (int i = 0; i < 10; i++) - Return values for success/failure (0/-1 convention)
- Interfacing with legacy APIs that expect
int
Even then, consider whether size_t (for array indexing) or a fixed-width type would be clearer.
The Anti-Pattern in Action
Here’s typical bad code:
// Anti-pattern: unclear sizes, non-portable
struct Packet {
int header;
int length;
int checksum;
};Fixed version:
// Correct: explicit sizes, portable, serialization-safe
#include <stdint.h>
struct Packet {
uint32_t header;
uint32_t length;
uint32_t checksum;
};What About Performance?
A common excuse: “But int is the native word size, so it’s faster!”
This was true decades ago. Modern compilers optimize fixed-width types just as well. Profile if you’re skeptical — you won’t find a difference in real workloads.
The tiny theoretical performance gain (if any) isn’t worth the portability and security risks.
Conclusion
Using int for anything beyond trivial local variables is a portability anti-pattern. The size is undefined, the behavior is unpredictable across platforms, and the security implications are severe.
Use <stdint.h> fixed-width types:
int32_t/uint32_tfor general 32-bit integersint64_t/uint64_tfor large values or timestampsint16_t/uint16_tfor memory-constrained environmentsint8_t/uint8_tfor byte manipulation
Your future self — and everyone who ports your code — will thank you.
What C anti-patterns have bitten you in production? Share your war stories.
