Variables & Data Types
Reviewed & published by Brayan K
By the end of this lesson you'll be able to store text, numbers, and true/false values in C++, choose the right type, convert safely between them, and read input from the user — the foundation of every C++ program you'll ever write.
Part of the free C++ course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
What You'll Learn
- Use the fundamental types: int, double, float, char, bool, std::string
- Declare and initialize variables, including modern brace init int x{5}
- Let the compiler infer types with auto
- Lock values with const and constexpr
- Understand signed vs unsigned and type sizes with sizeof
- Convert types safely with static_cast and read input with std::cin
💡 Real-World Analogy
A variable is a labelled container in a kitchen. A jar labelled "Sugar" (string) holds text; a measuring cup labelled "Cups" (int) holds whole numbers; a scale labelled "Weight" (double) holds decimals. Each container only holds one kind of thing — you can't pour flour into a liquid measuring cup. C++ is statically typed, which means every variable's type is fixed when you declare it and the compiler checks it before your program ever runs. That strictness is a feature: it catches whole classes of mistakes early.
📊 The Core Data Types
| Type | Holds | Size | Example |
|---|---|---|---|
| int | Whole numbers | 4 bytes | int age = 25; |
| double | Decimals (~15 digits) | 8 bytes | double h = 1.75; |
| float | Decimals (~7 digits) | 4 bytes | float t = 36.6f; |
| char | One character | 1 byte | char g = 'A'; |
| bool | true / false | 1 byte | bool ok = true; |
| std::string | Text | varies | string n = "Al"; |
Notice string uses "double quotes" but char uses 'single quotes' — mixing these up is the #1 beginner error, so it's worth burning in now. (string lives in the <string> header.)
1. Declaring & Initializing Variables
Every variable needs a type and a name, and you usually give it a value on the same line: type name = value;. Use descriptive camelCase names like firstName or totalPrice — code is read far more often than it's written, so a clear name pays for itself. Read this worked example, run it, then check the output against the comments.
#include <iostream>
#include <string>
using namespace std;
int main() {
// A variable = a named box that stores ONE type of value.
// Pattern: type name = value;
int age = 25; // whole number (no decimal point)
double price = 19.99; // decimal number (~15 digits, the default)
float temperature = 36.6f; // decimal, less precision -> note the 'f'
char grade = 'A'; // a SINGLE character -> 'single quotes'
bool isStudent = true; // true or false (prints as 1 / 0)
string name = "Alice"; // text -> always "double quotes"
// << feeds values into cout (the screen). endl adds a new line.
cout << "Name: " << name << endl; // Name: Alice
cout << "Age: " << age << endl; // Age: 25
cout << "Price: $" << price << endl; // Price: $19.99
cout << "Temp: " << temperature << endl; // Temp: 36.6
cout << "Grade: " << grade << endl; // Grade: A
cout << "Student? " << isStudent << endl; // Student? 1 (true)
return 0;
}
// ✅ Expected output:
// Name: Alice
// Age: 25
// Price: $19.99
// Temp: 36.6
// Grade: A
// Student? 1There's more than one way to give a variable its first value. The modern brace initialization — int x{5} — is preferred because it refuses to silently lose data (it blocks "narrowing", like squeezing 3.9 into an int). And auto lets the compiler pick the type for you from the value on the right.
#include <iostream>
#include <string>
using namespace std;
int main() {
// Three ways to create a variable:
int a = 5; // copy initialization (the classic =)
int b{10}; // brace initialization -> the modern, safer way
int c; // declare only -> value is GARBAGE until you set it
c = 15; // now c is safe to use
// auto: let the compiler work out the type from the value.
auto x = 42; // x is int (because 42 is a whole number)
auto y = 3.14; // y is double
auto greeting = string("Hi"); // greeting is a std::string
cout << a << " " << b << " " << c << endl; // 5 10 15
cout << x << " " << y << " " << greeting << endl; // 42 3.14 Hi
// Brace init also REFUSES to lose data silently:
// int oops{3.9}; // ERROR: narrowing 3.9 into an int is blocked
return 0;
}
// ✅ Expected output:
// 5 10 15
// 42 3.14 HiYour turn. The program below is almost complete — fill in the three blanks marked ___ using the hints in the comments, then run it.
#include <iostream>
#include <string>
using namespace std;
int main() {
// 🎯 YOUR TURN — replace each ___ then press "Try it Yourself".
// 1) Make an int called "year" set to the current year
int year = ___; // 👉 a whole number, e.g. 2026
// 2) Make a string called "city" set to a city name
string city = ___; // 👉 text in "double quotes"
// 3) Make a double called "price" set to 9.99
double price = ___; // 👉 a decimal number
// These lines already work once your variables exist:
cout << "In " << year << ", a coffee in " << city
<< " costs $" << price << endl;
// ✅ Expected output (example):
// In 2026, a coffee in London costs 9.99
return 0;
}2. Constants, Signedness & Sizes
Some values should never change — a tax rate, a maximum number of lives, pi. Mark them const and the compiler will reject any attempt to reassign them. constexpr goes one step further: the value must be known at compile time, which lets the compiler use it as a fixed size or array length. Numbers are also signed by default (they can be negative); marking one unsigned drops the negatives and reaches a bigger positive range with the same memory. sizeof tells you exactly how many bytes a type uses.
#include <iostream>
using namespace std;
int main() {
// const = a value that can NEVER change after it is set.
const double PI = 3.14159265;
// PI = 3.14; // ERROR: assignment of read-only variable 'PI'
// constexpr = known at COMPILE time (even stronger than const).
constexpr int MAX_PLAYERS = 100;
double radius = 5.0;
double area = PI * radius * radius; // 78.5398
cout << "Area: " << area << endl;
cout << "Max players: " << MAX_PLAYERS << endl;
// signed vs unsigned: unsigned cannot hold negatives, so it
// reaches a bigger positive range with the same memory.
int balance = -50; // signed (the default) -> can be negative
unsigned int lives = 3; // unsigned -> 0 and up only
cout << "Balance: " << balance << ", Lives: " << lives << endl;
// sizeof tells you how many bytes a type uses in memory.
cout << "int: " << sizeof(int) << " bytes" << endl; // 4
cout << "double: " << sizeof(double) << " bytes" << endl; // 8
cout << "char: " << sizeof(char) << " byte" << endl; // 1
cout << "bool: " << sizeof(bool) << " byte" << endl; // 1
return 0;
}
// ✅ Expected output:
// Area: 78.5398
// Max players: 100
// Balance: -50, Lives: 3
// int: 4 bytes
// double: 8 bytes
// char: 1 byte
// bool: 1 byte3. Type Conversion & Casting
Sometimes a value is the wrong type and you need to convert it. Implicit conversion happens automatically when it's safe (an int fits inside a double). When the conversion can lose data — like double to int — C++ may still do it but the decimals just vanish. To convert deliberately you use static_cast<type>(value), which states your intent clearly and is far safer than old C-style (int) casts. The classic trap: dividing two int values gives an int result.
#include <iostream>
using namespace std;
int main() {
// Implicit conversion — automatic and SAFE (no data lost).
int whole = 10;
double asDecimal = whole; // int -> double happens for you
cout << "Implicit: " << asDecimal << endl; // 10
// Narrowing — automatic but LOSES data (the decimals vanish).
double pi = 3.14159;
int truncated = pi; // 3 (chopped, NOT rounded)
cout << "Truncated: " << truncated << endl; // 3
// Integer division trap: both ints -> the remainder is dropped.
int total = 17, items = 5;
cout << "Wrong: " << total / items << endl; // 3 (not 3.4!)
// static_cast<type>(value) — you ASK for the conversion clearly.
double average = static_cast<double>(total) / items;
cout << "Right: " << average << endl; // 3.4
return 0;
}
// ✅ Expected output:
// Implicit: 10
// Truncated: 3
// Wrong: 3
// Right: 3.4Now you try. The two values below are int, so a plain / would chop the decimals off. Cast one side to double so the answer keeps its fraction:
#include <iostream>
using namespace std;
int main() {
// 🎯 YOUR TURN — both numbers are ints, so plain division truncates.
int distanceKm = 7;
int hours = 2;
// 1) Cast distanceKm to a double so the division keeps decimals
double speed = ___ / hours; // 👉 static_cast<double>(distanceKm)
cout << distanceKm << "km in " << hours << "h = "
<< speed << " km/h" << endl;
// ✅ Expected output: 7km in 2h = 3.5 km/h
return 0;
}4. Scope & Reading Input with cin
Scope is simply where a variable is visible. A variable lives inside the nearest pair of { } braces and disappears at the closing brace — try to use it outside and the compiler won't know what you mean. You read keyboard input with std::cin >> variable: the >> operator pulls what the user typed into your variable, automatically matching the variable's type (a number into an int, a word into a string).
#include <iostream>
#include <string>
using namespace std;
int main() {
// SCOPE = where a variable is visible. A variable lives inside the
// nearest { } block and disappears at the closing brace.
int outer = 1;
{
int inner = 2; // 'inner' only exists in this block
cout << outer << " " << inner << endl; // 1 2 (both visible)
}
// cout << inner; // ERROR: 'inner' was not declared in this scope
// cin reads typed input INTO a variable using >>
string name;
int birthYear;
cout << "Enter your name: ";
cin >> name; // reads one word into 'name'
cout << "Enter birth year: ";
cin >> birthYear; // reads a whole number into 'birthYear'
int age = 2026 - birthYear; // arithmetic with your variables
cout << "Hi " << name << ", you are about " << age << endl;
return 0;
}🔎 Deep Dive: auto vs spelling out the type
auto asks the compiler to deduce the type from the value: auto city = string("London"); is exactly a string. It's still strongly typed — once deduced, the type is fixed. Use auto to cut noise when the type is obvious or very long, but prefer the explicit type when it makes beginner code clearer.
const and constexpr both mean "do not reassign". const is read-only at run time; constexpr is fixed at compile time. Constants are written in UPPER_SNAKE_CASE by convention so they stand out.
auto greeting = string("Hi"); // compiler infers std::string
const int MAX_LIVES = 3; // read-only at run time
constexpr int BOARD = 8; // known at compile time
// MAX_LIVES = 5; // ❌ error: assignment of read-only variablePro Tips
- 💡 Prefer double over float: double is the default and far more precise; only use float when memory is genuinely tight.
- 💡 Always initialize: an uninitialized local like int x; holds a garbage value until you assign one. Give every variable a starting value.
- 💡 Use brace init for safety: int x{5} blocks narrowing conversions that int x = 5.9; would silently allow.
- 💡 Name things well: totalPrice tells a story; tp or x doesn't.
Common Errors (and the fix)
- Uninitialized variable (undefined behaviour): int x; cout << x; prints a random garbage value — and the program's behaviour is undefined. Always initialize: int x = 0; or int x{}.
- Integer division truncation: cout << 7 / 2; prints 3, not 3.5. Make one side a decimal — 7.0 / 2 — or use static_cast<double>(7) / 2.
- "narrowing conversion ... inside { }": brace init blocks lossy conversions, so int n{3.9}; won't compile. Use a double, or cast: int n{static_cast<int>(3.9)};.
- Wrong quotes: "A" (double quotes) is a string; 'A' (single quotes) is a char. Writing char c = "A"; fails to compile because the types don't match.
- "was not declared in this scope": you used a variable outside the { } block it was declared in. Declare it in an outer scope so it's still visible where you need it.
📋 Quick Reference
| Task | Code | Result |
|---|---|---|
| Declare integer | int x = 10; | 10 |
| Brace init | int x{5}; | 5 (safe) |
| Infer type | auto y = 3.14; | double |
| Constant | const int MAX = 100; | read-only |
| Safe cast | static_cast<double>(x) | double |
| Memory size | sizeof(int) | 4 |
| Read input | cin >> age; | into age |
Mini-Challenge: Profile Card
No blanks this time — just a brief and a blank canvas (with an outline to keep you on track). Build it, run it, and check your output against the example in the comments. This is exactly the kind of small program real apps are made of.
#include <iostream>
#include <string>
using namespace std;
int main() {
// 🎯 MINI-CHALLENGE: Profile card
// 1. Create variables: name (string), age (int),
// heightMeters (double), likesCoding (bool).
// 2. Print a tidy 4-line profile using cout << ... << endl.
// 3. BONUS: it is their birthday — add 1 to age with age = age + 1;
// then print "Next year you'll be " << age.
//
// ✅ Example output:
// Name: Sam
// Age: 20
// Height: 1.8m
// Likes coding: 1
// Next year you'll be 21
// your code here
return 0;
}🎉 Lesson Complete
- ✅ Fundamental types: int, double, float, char, bool, std::string
- ✅ "double quotes" for strings, 'single quotes' for a single char
- ✅ Brace init int x{5} blocks narrowing; auto infers the type
- ✅ const and constexpr lock values; unsigned drops negatives; sizeof shows bytes
- ✅ Implicit conversion is automatic; static_cast<type>(x) converts safely and clearly
- ✅ Variables live inside their { } scope; cin >> reads input into them
Practice quiz
Which type should you use to store a whole number like an age or a count?
- double
- int
- char
- bool
Answer: int. int holds whole numbers; double and float are for decimals.
What is the default floating-point type in C++ (about 15 digits of precision)?
- float
- decimal
- double
- real
Answer: double. double is the default floating-point type, with roughly 15 significant digits.
What is the difference between 'A' and "A" in C++?
- They are identical
- 'A' is a char, "A" is a std::string
- 'A' is a string, "A" is a char
- 'A' is invalid syntax
Answer: 'A' is a char, "A" is a std::string. Single quotes make a char (one character); double quotes make a std::string.
What does std::cout print for 7 / 2 when both are int?
- 3.5
- 3
- 4
- 3.0
Answer: 3. Two ints do integer division: the remainder is dropped, giving 3.
Which keyword lets the compiler infer a variable's type from its value?
- var
- auto
- let
- infer
Answer: auto. auto deduces the type from the initializer, e.g. auto x = 42; makes x an int.
What keyword marks a value that must be known at compile time?
- const
- static
- constexpr
- final
Answer: constexpr. constexpr is stronger than const: the value must be known at compile time.
How do you convert an int to a double deliberately and clearly?
- double(x)!
- static_cast<double>(x)
- convert<double>(x)
- x as double
Answer: static_cast<double>(x). static_cast<double>(x) states the conversion clearly and is safer than C-style casts.
What does brace initialization int n{3.9}; do?
- Sets n to 4
- Sets n to 3
- Fails to compile (narrowing blocked)
- Sets n to 3.9
Answer: Fails to compile (narrowing blocked). Brace init blocks narrowing, so squeezing 3.9 into an int won't compile.
What does sizeof(int) typically return on a 64-bit desktop?
- 1
- 2
- 4
- 8
Answer: 4. An int is typically 4 bytes; sizeof reports the byte size of a type.
What happens to a variable declared with 'int c;' but never assigned before use?
- It is automatically 0
- It holds a garbage value
- The program won't compile
- It becomes null
Answer: It holds a garbage value. An uninitialized local holds a garbage value until you assign one — always initialize.
Continue this course
- Previous: Introduction to C++
- Next: Data Types — Fundamental types, sizes, signed/unsigned, and modifiers
- Quick reference: C++ cheat sheet
Frequently asked questions
When should I use int, double, or float?
Use int for whole numbers (ages, counts, indexes). Use double for decimals — it is the default floating-point type in C++ and gives about 15 significant digits. Only reach for float when memory is very tight, because it keeps roughly half the precision.
Why did 7 / 2 give me 3 instead of 3.5?
Both 7 and 2 are int values, so C++ does integer division and throws away the remainder. Make one side a decimal — 7.0 / 2 — or cast it with static_cast<double>(7) / 2 to get 3.5.
What is the difference between 'A' and "A"?
'A' in single quotes is a char — exactly one character. "A" in double quotes is a std::string of length 1. They are different types, so mixing the quotes is a common beginner error.
Should I use auto for every variable?
No. auto is great when the type is obvious from the right-hand side or very long to write, but spelling out int or double often makes beginner code clearer. Use auto to remove noise, not to hide what a variable actually is.
What is the difference between const and constexpr?
const means the value cannot change after it is set. constexpr is stronger: the value must be known at compile time, so the compiler can bake it in. Use const for read-only values and constexpr for true compile-time constants like a maximum size.