C Data Types Reference

Size, signed/unsigned range, and printf format specifier for every basic C type and stdint.h fixed-width type, with a platform selector since int, long, and pointer sizes are not the same everywhere.

Type size and range table

char and short are 1 and 2 bytes on effectively every modern platform. int, long, long long, and pointer-sized types vary by data model, which is exactly the distinction this selector shows.

Basic types

Type Bytes Min Max printf

stdint.h fixed-width types

Type Bytes Min Max printf

*scanf requires %lf for double; printf accepts %f for both float and double because variadic float arguments are promoted to double automatically. PRId8/16/32/64 and PRIu8/16/32/64 are the portable macros; the %-specifier shown is what that macro expands to on the selected platform.

Why int and long change size across platforms

The C standard only guarantees minimum ranges for each type, not exact sizes, so compilers are free to make int and long as large as they like as long as the minimums are met. Linux and macOS chose to make long 64-bit on 64-bit systems (LP64), while Windows kept long at 32 bits for compatibility and only widened long long and pointers (LLP64), which is why identical C source can behave differently once compiled for each platform.

Why stdint.h exists

Fixed-width types like int32_t and uint64_t sidestep the int/long ambiguity entirely by guaranteeing an exact bit width on every conforming platform. Code that needs a specific size, such as a packet field or register value, should use these instead of plain int or long whenever portability matters.

A quirk worth knowing: is char signed or unsigned?

Unlike every other integer type here, plain char has implementation-defined signedness. It is commonly signed on x86 but unsigned by default on many ARM toolchains, which occasionally surfaces as a real bug when code assumes char behaves like signed char. Use signed char or unsigned char explicitly whenever the sign matters.

FAQ

Why does my long variable behave differently on Windows versus Linux?

Windows 64-bit uses the LLP64 data model, where long stays 32 bits, while 64-bit Linux and macOS use LP64, where long is 64 bits. Code that assumes long always matches pointer size or always holds 64-bit values will misbehave on Windows.

What is the actual range of an unsigned 32-bit integer?

0 to 4,294,967,295. This is a fixed value regardless of platform since it depends only on the bit width, which is why uint32_t is defined the same everywhere even though plain unsigned int's size is technically platform-dependent.

Should I use int or int32_t in new code?

Use int32_t (or another stdint.h fixed-width type) whenever the exact size matters, such as file formats, network protocols, or hardware registers. Plain int is fine for ordinary loop counters and small values where the exact width is irrelevant.

Why does printf use %f for both float and double?

C's variadic functions automatically promote float arguments to double, so by the time printf reads the argument it is always a double regardless of the variable's declared type, and %f handles that correctly. scanf does not promote its pointer arguments, which is why it needs %f for a float* and %lf for a double*.

What is the difference between size_t and ptrdiff_t?

size_t is unsigned and represents object sizes or array indices, which can never be negative. ptrdiff_t is signed and represents the difference between two pointers, which can be negative if one pointer is subtracted from an earlier one.

Related tools