Anti-Patterns

Why You Should Never Use 'int' in Modern C

The int type is a portability disaster — its size varies across platforms, causing silent bugs and security vulnerabilities. Use fixed-width types like int32_t instead.

Comparison between int and int32_t in C programming

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:

Platformsizeof(int)
Arduino (AVR)2 bytes (16-bit)
x86 Linux/Windows4 bytes (32-bit)
Most 64-bit systems4 bytes (32-bit)
Some embedded systems2 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);  -- BOOM

The 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 overflow

The 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 wrong

Binary 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, signed

Why Fixed-Width Types Are Better

  1. Portable — Same size on every platform
  2. Self-documentingint32_t tells you exactly what to expect
  3. Predictable overflow — You know when it will happen
  4. Binary compatibility — Safe for serialization and protocols
  5. 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_t for general 32-bit integers
  • int64_t / uint64_t for large values or timestamps
  • int16_t / uint16_t for memory-constrained environments
  • int8_t / uint8_t for 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.