A variable is a named box
When you write int score = 42;, three things happen: the compiler sets aside memory,
marks it as an integer, and gives that location the name score. You can read from it
or overwrite it. The CPU just sees an address — the name is for you.
Memory: [........][..42....][........]
↑
score
The four types you need first
C has a lot of types. Four cover almost everything early on:
| Type | Holds | Example value | Size (typical) |
|---|---|---|---|
int |
whole number, positive or negative | 42, -7, 0 |
4 bytes |
float |
number with a fractional part | 3.14f, -0.5f |
4 bytes |
char |
a single character (or a tiny integer 0-255) | 'A', '?', '\n' |
1 byte |
bool |
a truth value | true or false |
1 byte |
bool isn't built into C — include <stdbool.h> to get it. Without that header, C
programmers used int, where 0 is false and anything else is true.
#include <stdio.h>
#include <stdbool.h>
int main(void) {
int score = 95;
float pi = 3.14f; // 'f' marks it as float, not double
char grade = 'A';
bool passed = true;
printf("score=%d pi=%.2f grade=%c passed=%d\n",
score, pi, grade, passed);
return 0;
}The %d, %.2f, %c are format specifiers — they tell printf how to interpret each
argument. Get the type wrong and you get garbage, sometimes worse.
Declare, initialize, assign
Three things that look similar but aren't:
int x; // declare: reserve a box. Value is undefined garbage.
x = 5; // assign: put 5 in the box.
int y = 7; // declare and initialize together. Do this.
y = y + 1; // assign again: replace what's in the box.Always initialize. An uninitialized variable contains whatever bits happened to be in that memory — unpredictable, machine-dependent. Modern compilers warn about it. Treat that warning like an error.
Type rules and conversions
C silently converts between numeric types all the time. Convenient, and it'll bite you:
int n = 5;
float avg = n / 2; // integer division first. avg == 2.0, not 2.5
float ok = n / 2.0f; // float division. ok == 2.5The first one gets everyone. Both operands of / are int, so C throws away the
fractional part before storing the result. At least one operand has to be a float
to get float division.
A few more:
char c = 65;is the same aschar c = 'A';— A is ASCII 65.int n = 3.9f;stores3. Truncated, not rounded.- Mixing signed and unsigned types causes genuinely weird comparisons. More on that later.
'A' vs "A"
Nearly everyone mixes these up their first week:
char single = 'A'; // one character. Single quotes.
char *string = "A"; // a one-character string. Double quotes.'A' is one byte, value 65. "A" is two bytes: 'A' followed by a hidden '\0' that
marks the end of the string. Characters get single quotes, text gets double quotes.
We'll dig into strings properly in Arrays and Strings.
Gotchas
Uninitialized variables. Always initialize at declaration: int x = 0;, not int x;.
What's in an uninitialized variable is whatever happened to be in that memory — could be
anything. Compile with -Wall and treat that warning like an error.
Integer division. 5 / 2 is 2. The fraction disappears silently. Write 5 / 2.0f
when you need the decimal.
Float precision. 0.1f + 0.2f is not exactly 0.3f. Floats can't represent every
decimal, so don't use them for money or anything that has to match exactly. Use integers
instead (store cents, not dollars).
Format specifier mismatch. Passing a float to %d doesn't just print wrong —
it can corrupt the arguments that come after it. Match the specifier to the type, every time.
'A' vs "A". One is a char, one is a string. They're not interchangeable and
the compiler will sometimes let you mix them up without complaining.
TL;DR
- A variable is a named memory location with a type. The type tells the compiler the size and how to interpret the bits.
- Start with four types:
int,float,char,bool(needs<stdbool.h>). - Initialize when you declare. Uninitialized variables hold garbage.
5 / 2 == 2. Use5 / 2.0fwhen you want2.5.- Match
printfformat specifiers to the type, or expect nonsense.