C++ · Beginner
Variables and Types in C++
In short: A C++ variable is a named piece of storage with a type fixed at the point of declaration, such as int count = 0; or double price = 2.75;. The type decides which values fit, how much memory is used and which operations are allowed, and it cannot change afterwards. A variable declared without an initial value holds garbage until it is assigned.
Why every variable has a type
C++ is statically typed: when you declare a variable you name its type, and the compiler uses that type to decide how much memory to reserve, how to interpret the bits stored there and which operations make sense. int loaves = 23; reserves space for a whole number; double price = 2.75; reserves space for a number with a fractional part. Writing loaves = "many"; later is a compile error, not a run-time surprise. The type is part of the variable for its whole life.
The fundamental types you will use most are int for whole numbers, double for fractional numbers, char for a single character and bool for true or false. long long is a wider integer for values beyond about two billion. Text lives in std::string, which is not built into the language but comes from the library header <string>. A char literal uses single quotes, 'A'; a string literal uses double quotes, "Mill Lane".
The declaration type name = value; creates the variable and gives it a starting value in one step. C++11 added brace initialisation, int booked{31};, which does the same thing but refuses to silently lose information: int n{2.5}; is rejected by the compiler, whereas int n = 2.5; quietly stores 2. A declaration with no initialiser at all, int total;, is legal for local variables but leaves the value indeterminate; reading it before assigning is undefined behaviour and one of the classic C++ bugs. Give every variable a value when you declare it.
Two keywords refine declarations. const makes a variable read-only after initialisation, so const int maxSeats = 48; documents that the value never changes and lets the compiler reject any attempt to change it. auto (C++11) asks the compiler to deduce the type from the initialiser: auto remaining = maxSeats - booked; is an int because subtracting two ints gives an int. auto is handy when the type is long to spell, and safe when the initialiser makes the type obvious.
Types convert between each other by rules worth knowing early. An int placed where a double is expected converts silently and loses nothing. A double placed where an int is expected drops the fraction: 5.4 becomes 5, not 5 rounded but truncated toward zero. Writing static_cast<int>(total) makes such a conversion explicit, which tells readers it was intended. A char is really a small integer holding the character's code, so 'b' + 1 is the code for 'c', and printing a bool shows 1 or 0 unless you switch the stream to std::boolalpha.
Names may contain letters, digits and underscores, cannot start with a digit, cannot be keywords such as int or return, and are case sensitive. Choose names that state the meaning: pricePerLoaf or price_per_loaf rather than p.
Syntax
type name = value; // declare and initialise
type name{value}; // brace form: rejects narrowing conversions
type name; // legal, but the value is indeterminate
const type NAME = value; // cannot be assigned to after this line
auto name = expression; // type deduced from the expression
int count = 0;
double price = 2.75;
char grade = 'A';
bool open = true;
std::string city = "Leeds"; // needs #include <string>
long long population = 8000000000LL;The suffix LL marks an integer literal as long long. Without it, a literal too large for int is still given a wider type by the compiler, but the suffix makes the intent clear.
The types you will use first
| Type | Example | Size with GCC on 64-bit Linux | Notes |
|---|---|---|---|
| int | 42 | 4 bytes | whole numbers from -2,147,483,648 to 2,147,483,647 |
| long long | 12000000000LL | 8 bytes | whole numbers up to about 9.2 x 10^18 |
| double | 2.75 | 8 bytes | fractional numbers, about 15 significant digits; std::cout shows 6 by default |
| char | 'A' | 1 byte | one character, stored as its numeric code |
| bool | true | 1 byte | true or false; prints as 1 or 0 unless std::boolalpha is set |
| std::string | "Mill Lane" | varies | text of any length; requires #include <string> |
Declaring one of each
A bakery's stock record uses five types; watch how each one prints.
#include <iostream>
#include <string>
int main() {
int loaves = 23; // a whole number
double pricePerLoaf = 2.75; // a number with a fractional part
char grade = 'A'; // one character, in single quotes
bool sourdough = true; // true or false
std::string bakery = "Mill Lane Bakery";
long long yearlyCustomers = 12000000000LL;
std::cout << bakery << "\n";
std::cout << "Loaves: " << loaves << "\n";
std::cout << "Stock value: " << loaves * pricePerLoaf << "\n";
std::cout << "Grade: " << grade << "\n";
std::cout << "Sourdough: " << sourdough << "\n";
std::cout << std::boolalpha;
std::cout << "Sourdough: " << sourdough << "\n";
std::cout << "Customers: " << yearlyCustomers << "\n";
return 0;
}Output
Mill Lane Bakery Loaves: 23 Stock value: 63.25 Grade: A Sourdough: 1 Sourdough: true Customers: 12000000000
loaves * pricePerLoaf multiplies an int by a double; the int is converted to double first, so the result keeps its fraction. The bool prints as 1 until std::boolalpha switches the stream into printing true and false; the setting stays on for the rest of the program. Twelve billion does not fit in an int, which is why yearlyCustomers is a long long.
Reading typed input and converting
An order line is read as a word, an int and a double; then two conversions happen, one deliberate and one accidental.
#include <iostream>
#include <string>
int main() {
std::string item;
int quantity;
double unitPrice;
std::cin >> item >> quantity >> unitPrice;
double total = quantity * unitPrice;
std::cout << item << " x" << quantity << " = " << total << "\n";
int wholePounds = static_cast<int>(total); // the fraction is dropped
std::cout << "Whole pounds: " << wholePounds << "\n";
double ratio = quantity / 3; // int / int happens first
std::cout << "Ratio: " << ratio << "\n";
return 0;
}Input given to the program: croissant 4 1.35
Output
croissant x4 = 5.4 Whole pounds: 5 Ratio: 1
>> uses each variable's type to decide how to parse the text: item receives the word, quantity the int, unitPrice the double. static_cast<int>(total) deliberately truncates 5.4 to 5. The last line is the accidental case: quantity / 3 divides two ints, giving 1 with the remainder thrown away, and only then is the 1 stored in a double. Declaring the result as double does not make the division fractional; one of the operands must be a double, a point the operators lesson returns to.
const, auto and characters as numbers
A fixed capacity, two deduced types, and a look under the hood of char.
#include <iostream>
int main() {
const int maxSeats = 48;
int booked{31}; // brace initialisation
auto remaining = maxSeats - booked; // deduced as int
auto fillRate = booked / static_cast<double>(maxSeats); // deduced as double
std::cout << "Remaining: " << remaining << "\n";
std::cout << "Fill rate: " << fillRate << "\n";
// maxSeats = 50; would not compile: maxSeats is const
char letter = 'b';
letter = letter + 1; // a char is a small integer underneath
std::cout << "Next letter: " << letter << "\n";
std::cout << "Code of 'A': " << static_cast<int>('A') << "\n";
return 0;
}Output
Remaining: 17 Fill rate: 0.645833 Next letter: c Code of 'A': 65
auto gives remaining the type int and fillRate the type double, because that is what each expression produces. The cast on maxSeats is what makes the division fractional. std::cout prints doubles with six significant digits by default, so 0.6458333... appears as 0.645833. Adding 1 to 'b' yields the next character code, and casting 'A' to int reveals the code itself, 65 in the ASCII-compatible encoding this toolchain uses.
Common mistakes
Reading a variable that was never given a value
Why it goes wrong:
int total; total += 5;adds 5 to whatever bits happened to be in that memory. The program may print the right answer in one run and nonsense in the next, because reading an indeterminate value is undefined behaviour.-Wallsometimes warns (may be used uninitialized), but not always.Fix: Initialise at the point of declaration, even if only to 0.
C++ · fixint total = 0; total += 5;Expecting a fraction from two ints
Why it goes wrong:
double average = 7 / 2;stores 3, because the division happens between ints before the result is converted to double.Fix: Make one operand a double: write
7 / 2.0or cast a variable withstatic_cast<double>(sum) / count.Reading a full name with std::cin >>
Why it goes wrong:
>>stops at the first space, sostd::cin >> namegiven "Priya Natarajan" stores only "Priya" and leaves "Natarajan" waiting in the input for the next read.Fix: Use
std::getline(std::cin, name)when the value may contain spaces.Treating the character '5' as the number 5
Why it goes wrong:
char d = '5'; int n = d;stores 53, the character code of the digit, not 5.Fix: Subtract the code of '0':
int n = d - '0';gives 5, because the digit characters have consecutive codes.C++ · fixchar d = '5'; int n = d - '0'; // 5
Where you use this
Choosing types is the first design decision in any program, and money is the classic example. A shop till that stores prices as double will eventually add 0.10 to 0.20 and get 0.30000000000000004, because binary fractions cannot represent most decimal amounts exactly. The usual answer is to store amounts as whole pence in an int or long long and only format them with a decimal point when printing. Quantities are ints, a discount flag is a bool, the customer's name is a std::string, and the VAT rate that never changes during the run is a const. Getting these choices right up front removes a whole family of later bugs.
const int VAT_PERCENT = 20;
int pricePence = 275;
int quantity = 4;
bool memberDiscount = false;
std::string customer = "A. Bakshi";
int netPence = pricePence * quantity; // 1100, exact
int grossPence = netPence + netPence * VAT_PERCENT / 100; // 1320, exact
std::cout << "Total: " << grossPence << " pence\n"; // format as pounds only at the edgeKey points
- A declaration names a type and a variable; the type never changes.
int,double,char,boolandlong longare built in;std::stringneeds#include <string>.- Always initialise: an uninitialised local variable holds garbage, and reading it is undefined behaviour.
- Brace initialisation
int n{2.5}is refused by the compiler;int n = 2.5silently stores 2. constprevents later assignment;autodeduces the type from the initialiser.- int to double converts silently; double to int truncates toward zero; use
static_castto show the conversion is intended. - A
charis a small integer holding a character code, and aboolprints as 1 or 0 unlessstd::boolalphais set.
Try it yourself
Two temperature readings with decimal places are averaged, but the program stores them in the wrong type, so with the input 21.6 and 18.2 it prints 10. Change the type of the two variables so the output is the true average, 19.9.
#include <iostream>
int main() {
int morning, evening;
std::cin >> morning >> evening;
std::cout << "Average: " << (morning + evening) / 2 << "\n";
return 0;
}
21.6 18.2Average: 19.9#include <iostream>
int main() {
double morning, evening;
std::cin >> morning >> evening;
std::cout << "Average: " << (morning + evening) / 2 << "\n";
return 0;
}
Practise this
Exercises for this lesson are in the C++ practice set.
Related lessons
- C++ Syntax and Your First ProgramHow a C++ program is put together: #include, main(), statements, blocks and std::cout, plus how to compile and run it with g++ -std=c++17.8 min
- Operators in C++C++ arithmetic, comparison, logical and assignment operators: integer division and modulo, prefix versus postfix ++, short-circuit evaluation and precedence.9 min
- Strings in C++Working with std::string in C++: reading lines and words, length, indexing, concatenation, find and substr, comparison, changing case and converting numbers.10 min
Frequently asked questions
What happens if I use a C++ variable without initialising it?
A local variable declared without an initialiser, such as int n;, has an indeterminate value: whatever bits were already in that memory. Reading it is undefined behaviour, so the program might print 0, print a large random-looking number, or behave differently after a recompile. The compiler warns only in some cases. The cure is simple: give every variable a value when you declare it.
When should I use auto instead of writing the type?
Use auto when the initialiser already makes the type obvious or when the type is long to spell, which becomes common with iterators and templates later on. Avoid it when the literal would give a type you did not mean: auto x = 5; is an int, so x / 2 is 2, whereas double x = 5; would give 2.5.
Is int always 4 bytes in C++?
No. The standard only guarantees that int is at least 16 bits and that long long is at least 64 bits. On GCC for 64-bit Linux, and on the common desktop platforms, int is 32 bits, which holds values up to 2,147,483,647. When a total might exceed about two billion, use long long. sizeof(int) reports the size on the platform you are compiling for.
How this page was checked. Every program on it was run with GCC 13 (C++17) at build time by the publishing checks, and the output shown is what it printed. Running C++ inside the browser is not available yet, so the Run button is absent rather than pretending; copy the code and run it with GCC 13 (C++17) locally.