Introduction
A variable is a labelled box in the chip's memory. Its type decides two things: how big the box is, and what kind of thing may go in it.
On a laptop this rarely matters — there is memory to spare. An Arduino UNO has 2 kilobytes of it, about enough for two thousand letters. Choosing a type that is twice the size you need is a real cost.
Types also decide what happens when you do arithmetic. 7 / 2 is 3 if both numbers are whole, and 3.5 if either is decimal. Nothing warns you. The sketch compiles and quietly gives the wrong answer, which is the most expensive kind of mistake.
Then there is const. It is not a type — it is a promise that a value will never change. Break the promise and the compiler stops you, which is exactly what you want for a pin number you typed once and will never edit again.
What you will be able to do
By the end of this lesson you can:
- Choose between
int,long,float,boolandcharfor a given job. - Say how much memory each one takes.
- Predict what
7 / 2gives, and what7.0 / 2gives. - Recognise an overflow from its symptom.
- Use
constand explain what it protects you from. - Say why
unsigned longis the right type for a time.
What you need
| Part | Type | Qty |
|---|---|---|
| Arduino UNO R3 | Microcontroller | 1 |
| USB A to B cable | Cable | 1 |
Basic
No wiring. Read everything in the Serial Monitor at 9600 baud.
→ Variables in Arduino — the same ground more slowly, with a circuit.
The types you will use
| Type | Size | Holds | Example |
|---|---|---|---|
bool | 1 byte | true or false | bool ready = false; |
char | 1 byte | one character, −128 to 127 | char grade = 'A'; |
byte | 1 byte | 0 to 255 | byte level = 200; |
int | 2 bytes | −32768 to 32767 | int count = 500; |
unsigned int | 2 bytes | 0 to 65535 | unsigned int steps = 40000; |
long | 4 bytes | ±2.1 billion | long total = 100000; |
unsigned long | 4 bytes | 0 to 4.29 billion | unsigned long now = millis(); |
float | 4 bytes | decimals, ~7 digits | float temp = 28.5; |
double | 4 bytes | on an UNO, the same as float | double pi = 3.14159; |
⚠️ On an UNO, double is not more precise than float — they are both 4 bytes. On a Nano 33 or an ESP32 a double is genuinely 8 bytes. Code that relies on the difference behaves differently on different boards.
Whole numbers divide differently
int a = 7 / 2; // 3 — the .5 is thrown away, not rounded
float b = 7 / 2; // 3.0 — still 3, computed before storing
float c = 7.0 / 2; // 3.5 — one decimal makes the whole sum decimal
b is the one that catches people. The division happens before the answer is stored, and both numbers were whole, so the .5 was gone before float ever saw it.
Making just one side decimal fixes it: 7.0 / 2, or (float)7 / 2.
Overflow
A type that runs out of room does not warn you. It wraps.
int big = 32767;
big = big + 1;
Serial.println(big); // -32768
This is the bug behind "my timer went mad after half a minute": millis() in an int passes 32767 after 32.7 seconds and turns negative.
byte wraps at 255 to 0. unsigned int wraps at 65535.
bool
Holds true or false — one bit of meaning in one byte.
bool ledOn = false;
ledOn = true;
Anything non-zero counts as true, so bool x = 5; gives true. Useful to know, best not relied on.
char
One character, in single quotes. Double quotes mean something else entirely.
char grade = 'A'; // one character
Serial.println(grade); // A
Serial.println(grade, DEC); // 65
A char is a small number. 'A' is 65. That is why Serial.print() needs telling which you meant.
const
const says: this never changes.
const int LED_PIN = 9;
LED_PIN = 10; // error: assignment of read-only variable
The compiler refuses. That is the value of it — a mistake becomes an error message instead of a strange bug at midnight.
Use const for anything you set once: pin numbers, thresholds, timings.
Watch out for
7 / 2is 3. Make one side decimal when you want 3.5.intoverflows at 32767. Times and large counts wantlongorunsigned long.doubleisfloaton an UNO. Do not assume extra precision.floatis not exact.0.1 + 0.2is not quite0.3; never compare floats with==.'A'and"A"are different. Single quotes for one character, double for text.
Challenges
Challenge 1
Break an int on purpose.
Print 32767, then add one and print again. Then add one more.
Do the same with a byte starting at 255, and with an unsigned int starting at 65535.
For each, write down what you expected and what actually happened.
Then explain, in one sentence, why storing millis() in an int stops working after about 33 seconds.
Log in to ask for the answer.
Challenge 2
The division trap.
Print all four of these and note the answers:
7 / 2
7.0 / 2
7 / 2.0
(float)7 / 2
Three give 3.5 and one does not. Which, and why?
Then work out how many whole 250 ms slots fit in 1000 ms, and how many fit in 900 ms. Print both. Is the second answer what you wanted?
Log in to ask for the answer.
Challenge 3
Pick the right box.
For each of these, write down the type you would use and one sentence saying why:
- A pin number, set once and never changed.
- The number of milliseconds since the board started.
- Whether a door is open.
- A temperature like 27.4 °C.
- How many times a button has been pressed today.
- The letter grade a student got.
Then declare all six in a sketch and print each one. If any of them will not compile, that is part of the exercise.
Log in to ask for the answer.
Extra challenge
Count the memory.
Using the size column in the table, add up how many bytes this uses:
const int LED_PIN = 9;
unsigned long lastTime = 0;
float temperature = 0.0;
bool ready = false;
char grade = 'A';
int counts = 0;
Now check your answer with sizeof():
Serial.println(sizeof(lastTime));
An UNO has 2048 bytes of memory for variables. What percentage did those six use?
Think about it: you have 2048 bytes. A single float for every one of 500 sensor readings would need 2000 of them, leaving almost nothing for anything else. What could you store instead that would still be useful?
Log in to ask for the answer.