Menu

Data Types in C: int, float, char, double, and More

Every C variable has a type that fixes its size and what it can hold. Here are all the basic types, their real sizes, signed versus unsigned, sizeof, limits.h, and what happens on overflow.

This page includes runnable editors - edit, run, and see output instantly.

Every variable in C has a type, chosen when you declare it and fixed for its lifetime. The type decides three things: how many bytes the variable occupies, how those bytes are interpreted, and what operations make sense on it.

C's type list is short. The complexity is in the modifiers.

The Four Base Types

  • char holds one byte. It is used for single characters ('A'), for the bytes of a string, and sometimes as a tiny integer.
  • int is the workhorse whole-number type. Loop counters, sizes, IDs.
  • float and double hold real numbers. double has roughly twice the precision.
  • void is the fourth type in a sense, but it means "no value" - you cannot declare a void variable, only use it as a function return type, an empty parameter list, or a typeless pointer.

Note the f suffix on 3.14f. Without it the literal is a double, and assigning it to a float loses precision silently. Some compilers warn about it.

Modifiers: short, long, unsigned

The base integer types come with modifiers that change the size or the sign.

Each integer type needs its own format specifier: %hd for short, %d for int, %ld for long, %lld for long long, %u for unsigned. Using the wrong one is undefined behavior, not a rounding error.

unsigned removes the sign bit and doubles the positive range. An unsigned int holds 0 to about 4.3 billion instead of -2.1 to +2.1 billion. It is the right choice for things that genuinely cannot be negative - sizes, counts of bytes, bit patterns - and a trap for anything that might be subtracted below zero.

signed is the default for int, short, long, and long long, so you almost never write it. The one place it matters is char: whether plain char is signed or unsigned is implementation-defined, so write signed char or unsigned char when the sign matters.

Sizes: What You Actually Get

The C standard specifies minimums, not exact sizes. On any modern 64-bit Linux, macOS, or Windows machine you will see this:

TypeTypical sizeTypical range
char1 byte-128 to 127 (or 0 to 255)
short2 bytes-32,768 to 32,767
int4 bytes-2,147,483,648 to 2,147,483,647
long8 bytes (4 on Windows)roughly ±9.2 quintillion
long long8 bytesroughly ±9.2 quintillion
float4 bytes~7 significant digits
double8 bytes~15 significant digits
long double16 bytes (varies)more than double

The long row is the one that catches people: it is 8 bytes on Linux and macOS, and 4 bytes on 64-bit Windows. Code that assumes long can hold a 64-bit value is not portable. Use long long, or the exact-width types from stdint.h (int32_t, uint64_t) when the size is part of the requirement.

sizeof: Ask the Compiler

Never guess a size - measure it:

sizeof is an operator, not a function, and it is evaluated at compile time. It yields a value of type size_t, which prints with %zu.

sizeof(char) is guaranteed to be exactly 1 - that is the definition of a byte in C. Everything else is measured relative to it.

limits.h and float.h

The exact ranges for your compiler are available as named constants:

These are the values to compare against when you need to know whether an operation will overflow. Checking if (a > INT_MAX - b) before computing a + b is how you detect overflow before it happens - which matters, because you cannot detect signed overflow after.

Integer Overflow

What happens when a value exceeds its type's range depends entirely on the sign.

Unsigned overflow is defined: the value wraps modulo 2^N.

That second case is a real source of bugs. A loop like for (unsigned i = n - 1; i >= 0; i--) never ends, because an unsigned value is always >= 0.

Signed overflow is undefined behavior. Not "wraps around" - undefined. The compiler is allowed to assume it never happens and optimize on that basis, which means an overflow check written after the fact can be deleted:

int sum = a + b;
if (sum < a) { /* the compiler may remove this entirely */ }

Check before, using the limits:

if (b > 0 && a > INT_MAX - b) {
    /* a + b would overflow - handle it */
}

Floating-Point Precision

float and double store numbers in binary, and most decimal fractions have no exact binary form - the same way 1/3 has no exact decimal form.

The rule that follows: never compare floating-point values with ==. Compare the absolute difference against a small tolerance instead.

And never use floating-point for money. Store cents as an integer; a long long of cents is exact where a double of dollars is not.

Choosing a Type

A short decision list that covers most code:

  • Whole numbers: int unless you have a reason. It is the type the CPU handles most efficiently and the type that all the arithmetic rules are built around.
  • Anything bigger than 2 billion: long long, or int64_t from stdint.h.
  • Sizes, lengths, array indices from sizeof or strlen: size_t. It is unsigned and guaranteed large enough for any object.
  • Decimals: double. Use float only to halve memory in large arrays or on embedded hardware without a double-precision unit.
  • Single characters and raw bytes: char for text, unsigned char for binary data.
  • True/false: bool from stdbool.h - see booleans in C.
  • Exact bit widths (file formats, network protocols, hardware registers): stdint.h - uint8_t, int16_t, uint32_t, and so on.

Mixing Types

When you combine two different types in one expression, C converts them behind your back before doing the arithmetic. That is usually helpful and occasionally disastrous:

The first is integer division: both operands are int, so the result is an int and the fraction is discarded. The third is worse - comparing a signed value with an unsigned one converts the signed one to unsigned, turning -1 into a huge positive number.

Those conversion rules, and how to take control of them with explicit casts, are the subject of type casting in C.

Frequently Asked Questions

What are the basic data types in C?

Four base types: char for single characters and bytes, int for whole numbers, float and double for decimals. Modifiers change their size and sign - short, long, long long, signed, and unsigned - producing the full set.

How many bytes is an int in C?

Almost always 4 bytes (32 bits) on modern desktop and server systems, giving a range of about -2.1 billion to 2.1 billion. The standard only guarantees at least 2 bytes, and 16-bit microcontrollers do use 2. Use sizeof(int) if you need to know for certain.

What is the difference between float and double in C?

float is 4 bytes with about 7 significant decimal digits; double is 8 bytes with about 15. Double is the default for floating-point literals and math functions, and unless you are storing millions of values or targeting an embedded chip, double is the right choice.

What happens when an int overflows in C?

For a signed int, overflow is undefined behavior - the compiler may wrap, saturate, or optimize the check away entirely. For an unsigned int it is fully defined: the value wraps around modulo 2^N, so UINT_MAX + 1 is 0. Never rely on signed overflow.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED