Data Types in C
October 3, 2026 • 5 min read

Table of contents
Part of the series:C Explained
A data type tells the compiler two things: how many bytes a value occupies in memory, and how those bytes should be interpreted. Because those two facts are fixed, the type also decides the range of values a variable can hold. This post covers the basic types C gives you: integers, characters, floating-point numbers, and booleans.
The basic types
C has five fundamental types:
char- one byte (usually 8 bits), used for a single character like'A'.int- an integer, typically 4 bytes.float- a single-precision decimal number, 4 bytes.double- a double-precision decimal number, 8 bytes. Prefer it overfloatfor general math.void- “no type”. It cannot hold a value; it is used to mean “returns nothing” or “no parameters”, and later for generic pointers.
char letter = 'A';
int count = 42;
float pi = 3.14f; // the f marks a float literal
double precise = 3.14159;
Sizes and ranges
The list above says “usually” because C does not fix the sizes; it guarantees only minimum widths. On modern desktop machines, the de-facto sizes are:
type size range (signed)
char 1 byte -128 to 127
int 4 bytes -2,147,483,648 to 2,147,483,647
float 4 bytes ±3.4e38 (about 7 significant digits)
double 8 bytes ±1.8e308 (about 15 significant digits)
You can ask the compiler instead of guessing, with the sizeof operator:
#include <stdio.h>
int main(void) {
printf("int: %zu bytes\n", sizeof(int));
printf("double: %zu bytes\n", sizeof(double));
return 0;
}
sizeof returns bytes, and %zu is the specifier for that kind of value.
Modifiers: signed, unsigned, short, long
You can modify the basic types:
signedaccepts negatives;unsignedonly zero and positives, doubling the upper range because the sign bit is freed for value.shortmakes an integer smaller (usually 2 bytes);longmakes it bigger (usually 8 bytes).
unsigned int score = 4294967295U; // 0 to ~4.29 billion
short small = 32767; // usually 2 bytes
long big = 3000000000L; // usually 8 bytes
long long huge = 9000000000000LL; // at least 8 bytes
A char can also be signed or unsigned, which matters when you read raw bytes. The combined rules are easy to check mentally: an unsigned 8-bit value goes 0..255, a signed 8-bit value goes -128..127.
Overflow: what happens when you go past the range
If arithmetic produces a number too big for the type, the bits wrap around. With int you get undefined behavior in C, so the program may do anything; with unsigned the result wraps deterministically like an odometer:
unsigned char x = 255;
x = x + 1;
printf("%u\n", x); // prints 0
This is a feature on purpose for low-level code, and a footgun everywhere else. If your values could approach the edge of the range, use a wider type.
Booleans
C historically had no boolean type; any non-zero value is true and zero is false. That rule still applies everywhere in C. Since C99 there is a real _Bool, normally used through stdbool.h, which defines bool, true, and false:
#include <stdbool.h>
bool active = true;
bool done = false;
Exactly two values, and a bool printed with %d shows 1 or 0.
Converting between types
C converts types automatically in many situations, called implicit conversion. Assigning an int value into a double variable promotes it losslessly; going the other way truncates:
double d = 7; // 7 becomes 7.0
int i = 2.99; // becomes 2 (the fraction is dropped, not rounded)
When you mix types in arithmetic, the smaller type is promoted so both operands share a type; this is called usual arithmetic conversions1. The integer types get promoted to int or unsigned int, then to the floating-point type if one operand is a float.
You can also force a conversion explicitly with a cast:
int total = 7;
int items = 2;
double average = (double)total / items; // 3.5, not 3
Without the cast, 7 / 2 is integer division and yields 3, because both operands are int. The cast makes one operand a double, so the division becomes floating-point. This single cast fixes one of the most common beginner bugs in C.
Integer division deserves attention
For integers, / cuts off the fraction entirely (7 / 2 == 3), and % gives the remainder (7 % 2 == 1). The remainder keeps the sign of the dividend. Mixing an integer and a float promotes the integer, so 7.0 / 2 is 3.5.
The big picture
Types are the contract between your program and memory: char for single bytes, int and its modified forms for whole numbers, float/double for decimals, bool for flags, and void for “nothing”. The compiler checks types at build time, so getting them right catches a huge class of bugs before your program ever runs. Next, the tools for combining these values: operators.
Footnotes
/sponsor
Enjoyed this post? You can sponsor me and this site.