Loading
All articles
Programming 17 min read

How to Create a Variable in C++: A Visual, Hands-On Guide

Learn C++ variables visually: declaring and initializing, the main types (int, double, bool, char, std::string), how big each one is, integer division, overflow, decimal precision, brace initialization, auto, const, naming rules, real compiler messages, and exercises with answers.

nbkhope
On this page

A program that can't remember anything isn't very useful. A game needs to remember your score, a shop needs to remember what's in your cart, and a calculator needs to remember the numbers you typed. In C++, you remember things with variables.

In this guide you'll create variables of every basic type, see how much memory each one takes, and run into the classic surprises: whole-number division, numbers that overflow, decimals that aren't quite exact, and variables used before they get a value. Along the way you'll read real compiler messages, and you'll finish with exercises.

All outputs below come from real runs with GCC 14.1 (g++ -std=c++20) on 64-bit Windows. New to C++? Start with Writing Your First Program in C++.

A variable is a labeled box

A variable is a labeled box in memory. The declaration int age = 35; has three parts: the type int, which says what kind of data the box holds and how big it is, 4 bytes; the name age, the label you use in your code; and the initial value 35, what goes in the box. In memory, age is 4 bytes at some address, holding 35. Next to it, double height = 1.75 takes 8 bytes, bool isStudent = true takes 1 byte, and char grade = 'A' takes 1 byte. The type decides how many bytes the box has and what the bits inside mean

To create a variable you write its type, then its name, and usually an initial value:

1#include <iostream>
2#include <string>
3
4int main() {
5    int age = 35;
6    double height = 1.75;
7    bool isStudent = true;
8    char grade = 'A';
9    std::string name = "Roger";
10
11    std::cout << name << " is " << age << " years old and " << height << " m tall.\n";
12    std::cout << "Grade: " << grade << "\n";
13    std::cout << "Student? " << isStudent << "\n";
14    return 0;
15}
Roger is 35 years old and 1.75 m tall.
Grade: A
Student? 1
  • int holds whole numbers: 35, -7, 2024.
  • double holds numbers with a decimal point: 1.75, -0.5, 3.14159.
  • bool holds true or false. It prints as 1 or 0 (more on that below).
  • char holds a single character, written in single quotes: 'A'.
  • std::string holds text, written in double quotes. It needs #include <string>.

The type matters because C++ is statically typed: once a variable is an int, it's an int forever, and the compiler checks every use of it.

Declaring, assigning and changing

You can declare a variable first and give it a value later, then change it as often as you like. The = sign means "store this value", not "is equal to" (checking equality is ==):

1#include <iostream>
2
3int main() {
4    int score;
5    score = 10;
6    std::cout << "Start: " << score << "\n";
7
8    score = score + 5;
9    std::cout << "After a bonus: " << score << "\n";
10
11    score += 20;
12    score -= 3;
13    score *= 2;
14    std::cout << "After more changes: " << score << "\n";
15
16    score++;
17    std::cout << "Plus one: " << score << "\n";
18    return 0;
19}
1Start: 10
2After a bonus: 15
3After more changes: 64
4Plus one: 65

score = score + 5 reads the current value, adds 5, and stores the result back. The compound assignments +=, -=, *= and /= are shortcuts for exactly that, and ++ adds one.

Always give a variable a value

A variable that's declared but never given a value holds whatever bits happened to be in that memory: garbage. Reading it is a bug, and the compiler warns you if warnings are on:

1#include <iostream>
2
3int main() {
4    int total;
5    total = total + 10;
6    std::cout << total << "\n";
7    return 0;
8}
1$ g++ -std=c++20 -Wall -Wextra uninit.cpp -o uninit
2uninit.cpp: In function 'int main()':
3uninit.cpp:5:11: warning: 'total' is used uninitialized [-Wuninitialized]
4    5 |     total = total + 10;
5      |     ~~~~~~^~~~~~~~~~~~
6uninit.cpp:4:9: note: 'total' was declared here
7    4 |     int total;
8      |         ^~~~~

The program still builds, but what it prints is unpredictable. The fix is a habit: initialize every variable when you declare it, as in int total = 0;.

How big is each type?

Each type uses a fixed number of bytes, and that limits what it can hold. The sizeof operator tells you a type's size, and std::numeric_limits its range:

1#include <iostream>
2#include <limits>
3#include <string>
4
5int main() {
6    std::cout << "bool:        " << sizeof(bool) << " byte\n";
7    std::cout << "char:        " << sizeof(char) << " byte\n";
8    std::cout << "short:       " << sizeof(short) << " bytes, up to " << std::numeric_limits<short>::max() << "\n";
9    std::cout << "int:         " << sizeof(int) << " bytes, up to " << std::numeric_limits<int>::max() << "\n";
10    std::cout << "long:        " << sizeof(long) << " bytes, up to " << std::numeric_limits<long>::max() << "\n";
11    std::cout << "long long:   " << sizeof(long long) << " bytes, up to " << std::numeric_limits<long long>::max() << "\n";
12    std::cout << "float:       " << sizeof(float) << " bytes, about " << std::numeric_limits<float>::digits10 << " exact digits\n";
13    std::cout << "double:      " << sizeof(double) << " bytes, about " << std::numeric_limits<double>::digits10 << " exact digits\n";
14    std::cout << "std::string: " << sizeof(std::string) << " bytes (plus the text, if it's long)\n";
15    return 0;
16}
1bool:        1 byte
2char:        1 byte
3short:       2 bytes, up to 32767
4int:         4 bytes, up to 2147483647
5long:        4 bytes, up to 2147483647
6long long:   8 bytes, up to 9223372036854775807
7float:       4 bytes, about 6 exact digits
8double:      8 bytes, about 15 exact digits
9std::string: 32 bytes (plus the text, if it's long)
How big is each C++ type? Sizes in bytes measured with GCC 14.1 on 64-bit Windows. bool: 1 byte. char: 1 byte. short: 2 bytes, up to 32,767. int: 4 bytes, up to about 2.1 billion. long: 4 bytes on Windows but 8 bytes on 64-bit Linux and macOS. long long: 8 bytes, up to about 9.2 quintillion. float: 4 bytes, about 6 exact digits. double: 8 bytes, about 15 exact digits. std::string: 32 bytes for the object itself; long text is stored separately. Each extra byte doubles the range 256 times

Two things stand out:

  • An int tops out at 2,147,483,647, a bit over 2 billion. Fine for ages and scores, not for the world's population (about 8 billion).
  • long is 4 bytes on Windows but 8 bytes on 64-bit Linux and macOS. The C++ standard only sets minimums, so sizes can differ between systems. When you need an exact size, use long long (at least 8 bytes) or the fixed-width types like std::int64_t from <cstdint>.

Surprises with numbers

Whole-number division

When both sides of / are integers, C++ does integer division and throws away the remainder. % gives the remainder:

1#include <iostream>
2
3int main() {
4    int pizzas = 7;
5    int people = 2;
6    std::cout << "Each gets " << pizzas / people << " whole pizzas\n";
7    std::cout << "Left over: " << pizzas % people << "\n";
8    std::cout << "Exact share: " << static_cast<double>(pizzas) / people << "\n";
9    return 0;
10}
Each gets 3 whole pizzas
Left over: 1
Exact share: 3.5

static_cast<double>(pizzas) converts the value to a double for that calculation, so the division keeps the decimals.

Numbers that don't fit

Try to store 3 billion in an int:

1#include <iostream>
2
3int main() {
4    int tooBig = 3000000000;
5    long long bigEnough = 3000000000;
6    std::cout << tooBig << "\n";
7    std::cout << bigEnough << "\n";
8    return 0;
9}
-1294967296
3000000000
What happens when a number does not fit. An int has 32 bits and holds values from about -2.1 billion to +2.1 billion. 3,000,000,000 needs more than that, so when it is squeezed into an int only the low 32 bits are kept, and the top bit is read as the sign: the value wraps around to -1,294,967,296. That is 3,000,000,000 minus 2 to the power 32. A long long has 64 bits and holds it with room to spare. GCC compiled this with no error and no warning, even with -Wall -Wextra

No error, and not even a warning with -Wall -Wextra: the value silently wraps around to a negative number. Choose a type that's big enough for your data. (Overflowing an int during arithmetic, like adding 1 to the maximum, is even worse: it's undefined behavior, meaning the program can do anything.)

Decimals aren't exact

double stores numbers in binary, and most decimal fractions, like 0.1, can't be written exactly in binary, just as 1/3 can't be written exactly in decimal:

1#include <iomanip>
2#include <iostream>
3
4int main() {
5    double sum = 0.1 + 0.2;
6    std::cout << sum << "\n";
7    std::cout << std::setprecision(17) << sum << "\n";
8    std::cout << (sum == 0.3) << "\n";
9
10    float third = 1.0f / 3;
11    double betterThird = 1.0 / 3;
12    std::cout << std::setprecision(10) << third << " vs " << betterThird << "\n";
13    return 0;
14}
10.3
20.30000000000000004
30
40.3333333433 vs 0.3333333333
float vs. double precision. The value one third printed with 10 digits, measured with GCC 14.1: float, 4 bytes, gives 0.3333333433, wrong after about 7 digits; double, 8 bytes, gives 0.3333333333, correct to about 16 digits. And 0.1 + 0.2 as a double is really 0.30000000000000004, so comparing it to 0.3 with == is false. Prefer double over float, and compare decimals with a small tolerance instead of ==

std::cout rounds to 6 significant digits by default, which hides the error; std::setprecision(17) shows it. Two lessons: use double rather than float unless you have a reason, and don't compare decimals with ==; check that they're close instead. (For money, programs usually count whole cents in an integer.)

bool and char are numbers in disguise

1#include <iostream>
2
3int main() {
4    bool isOpen = true;
5    std::cout << isOpen << "\n";
6    std::cout << std::boolalpha << isOpen << "\n";
7
8    char letter = 'A';
9    std::cout << letter << " has code " << static_cast<int>(letter) << "\n";
10    char next = letter + 1;
11    std::cout << "The next letter is " << next << "\n";
12    return 0;
13}
11
2true
3A has code 65
4The next letter is B

A bool prints as 1 or 0 unless you send std::boolalpha to the stream first. A char is really a small number (65 is the code for A in ASCII), so you can do arithmetic with it.

Ways to initialize

C++ has several initialization styles. The two you'll see most:

1#include <iostream>
2
3int main() {
4    int withEquals = 3.7;
5    int withBraces{42};
6    double ratio{0.5};
7    std::cout << withEquals << " " << withBraces << " " << ratio << "\n";
8    return 0;
9}
3 42 0.5

The = version quietly chopped 3.7 down to 3. Brace initialization { } refuses to lose information like that, so a mistake becomes a compiler error instead of a silent bug:

1int main() {
2    int score{3.7};
3    return score;
4}
1narrow.cpp: In function 'int main()':
2narrow.cpp:2:15: error: narrowing conversion of '3.7000000000000002e+0' from 'double' to 'int' [-Wnarrowing]
3    2 |     int score{3.7};
4      |               ^~~

That's a narrowing conversion error. (Notice the compiler printing 3.7 as 3.7000000000000002: the decimal problem again!) Many modern style guides recommend braces for exactly this reason.

Letting the compiler pick: auto

With auto, the compiler figures out the type from the initial value:

1#include <iostream>
2#include <string>
3
4int main() {
5    auto count = 10;
6    auto price = 4.99;
7    auto ready = false;
8    auto title = std::string("C++ Variables");
9    std::cout << count << " items at " << price << " each\n";
10    std::cout << title << ", ready: " << std::boolalpha << ready << "\n";
11    return 0;
12}
10 items at 4.99 each
C++ Variables, ready: false

count is an int, price a double, ready a bool and title a std::string. The variable is still strictly typed; you just don't spell the type out. Note that auto title = "C++ Variables"; without std::string(…) would make a plain C-style character array instead of a std::string, one reason to use auto thoughtfully.

Values that never change: const

Mark a variable const when its value should never change after it's set. The compiler then refuses any attempt to change it:

1int main() {
2    const double pi = 3.14159;
3    pi = 3;
4    return 0;
5}
1constant.cpp: In function 'int main()':
2constant.cpp:3:8: error: assignment of read-only variable 'pi'
3    3 |     pi = 3;
4      |     ~~~^~~

Use const generously: it documents your intent and turns accidental changes into compile errors. (For values known at compile time, constexpr goes one step further and guarantees the value is computed while compiling.)

Naming your variables

Rules and conventions for C++ variable names. Valid: score, playerName, max_speed, _count, level2. Invalid: 2ndPlace, which starts with a digit; player name, which contains a space; total-cost, where the minus sign means subtraction; and class, which is a reserved keyword. Names are case-sensitive, so score, Score and SCORE are three different variables. Popular conventions: camelCase like totalPrice, and snake_case like total_price; pick one and use it consistently. Prefer descriptive names: secondsElapsed beats s

The rules:

  • Use letters, digits and underscores, starting with a letter or underscore.
  • No spaces, no symbols like - or $, and no keywords (int, class, return, new…).
  • Names are case-sensitive: score, Score and SCORE are three different variables.

Break a rule and the compiler stops you:

1int main() {
2    int 2ndPlace = 2;
3    return 0;
4}
1badname.cpp: In function 'int main()':
2badname.cpp:2:9: error: expected unqualified-id before numeric constant
3    2 |     int 2ndPlace = 2;
4      |         ^~~~~~~~

Beyond the rules, choose descriptive names. secondsElapsed tells the next reader (often you, a month later) exactly what it holds; s doesn't. The two common styles are camelCase and snake_case; pick one and stay consistent.

Your turn: practice

Try these before peeking at the answers. Compile each with g++ -std=c++20 -Wall -Wextra.

Exercise 1: Predict the output (warm-up). What does this print?

1#include <iostream>
2
3int main() {
4    int apples = 10;
5    int friends = 4;
6    apples -= 2;
7    std::cout << apples / friends << " " << apples % friends << "\n";
8    double average = apples / friends;
9    std::cout << average << "\n";
10    return 0;
11}

Exercise 2: Swap two values. Given int left = 3; and int right = 8;, swap their values so that left is 8 and right is 3, then print them. (Hint: you'll need a third variable.)

Exercise 3: Circle calculator. Read a radius (a whole number) and print the circle's area and circumference, using a const double pi = 3.14159;.

Exercise 4: Seconds to hours. Read a number of seconds and print it as hours, minutes and seconds. For example, 3725 seconds is 1 h 2 min 5 s.

Exercise 5: Fix the program (challenge). This should print the price of 3 tickets at 12.50 each and the number of seats left out of 150, but it has three problems. Find them.

1#include <iostream>
2
3int main() {
4    const int seats = 150;
5    int ticketPrice{12.50};
6    int tickets = 3;
7    seats = seats - tickets;
8    std::cout << "Total: " << tickets * ticketPrice << "\n";
9    std::cout << "Seats left: " << seats << "\n";
10    return 0;
11}

Quick quiz

  1. What are the three parts of int age = 35;?
  2. What's the difference between = and ==?
  3. Why does 7 / 2 give 3 in C++?
  4. Why is int score{3.7}; a compile error but int score = 3.7; isn't?
  5. What does const do?

Answers

Exercise 1: After apples -= 2, apples is 8. 8 / 4 is 2 with remainder 0. The trap is the last line: apples / friends is computed as integer division first (giving 2), and only then stored in the double:

1#include <iostream>
2
3int main() {
4    int apples = 10;
5    int friends = 4;
6    apples -= 2;
7    std::cout << apples / friends << " " << apples % friends << "\n";
8    double average = apples / friends;
9    std::cout << average << "\n";
10    return 0;
11}
2 0
2

Exercise 2: Save one value before overwriting it:

1#include <iostream>
2
3int main() {
4    int left = 3;
5    int right = 8;
6    int saved = left;
7    left = right;
8    right = saved;
9    std::cout << "left = " << left << ", right = " << right << "\n";
10    return 0;
11}
left = 8, right = 3

(The standard library also has std::swap(left, right) in <utility>.)

Exercise 3: The radius is an int, but multiplying by the double pi makes the results doubles:

1#include <iostream>
2
3int main() {
4    const double pi = 3.14159;
5    int radius = 0;
6    std::cout << "Radius: ";
7    std::cin >> radius;
8    double area = pi * radius * radius;
9    double circumference = 2 * pi * radius;
10    std::cout << "Area: " << area << "\n";
11    std::cout << "Circumference: " << circumference << "\n";
12    return 0;
13}
Radius: 10
Area: 314.159
Circumference: 62.8318

Exercise 4: Integer division and the remainder operator do all the work:

1#include <iostream>
2
3int main() {
4    int totalSeconds = 0;
5    std::cout << "Seconds: ";
6    std::cin >> totalSeconds;
7    int hours = totalSeconds / 3600;
8    int minutes = totalSeconds % 3600 / 60;
9    int seconds = totalSeconds % 60;
10    std::cout << hours << " h " << minutes << " min " << seconds << " s\n";
11    return 0;
12}
Seconds: 3725
1 h 2 min 5 s
Seconds: 86399
23 h 59 min 59 s

Exercise 5: (1) int ticketPrice{12.50} is a narrowing error: a price with cents needs a double. (2) seats is const, so it can't be changed: keep the total as a constant and compute the seats left in a new variable. (3) With those fixed, it works:

1#include <iostream>
2
3int main() {
4    const int seats = 150;
5    double ticketPrice{12.50};
6    int tickets = 3;
7    int seatsLeft = seats - tickets;
8    std::cout << "Total: " << tickets * ticketPrice << "\n";
9    std::cout << "Seats left: " << seatsLeft << "\n";
10    return 0;
11}
Total: 37.5
Seats left: 147

Quiz: (1) The type (int), the name (age) and the initial value (35). (2) = stores a value in a variable; == asks whether two values are equal. (3) Both numbers are integers, so C++ does integer division and drops the remainder; make one a double to keep the decimals. (4) Brace initialization forbids narrowing conversions that lose information; = silently truncates 3.7 to 3. (5) It makes the variable read-only after it's initialized; any attempt to change it is a compile error.

Cheat sheet

I want to…Write
A whole numberint count = 0;
A really big whole numberlong long population = 8000000000;
A number with decimalsdouble price = 4.99;
True or falsebool isReady = false;
One characterchar initial = 'R';
Textstd::string name = "Roger"; (with #include <string>)
Refuse lossy conversionsint score{42};
Let the compiler pick the typeauto total = 0.0;
A value that never changesconst double pi = 3.14159;
Change a valuescore = 10; / score += 5; / score++;
Divide keeping decimalsstatic_cast<double>(pizzas) / people
See a type's sizesizeof(int)
Print true / falsestd::cout << std::boolalpha << isReady;

Wrapping up

Variables are how programs remember things:

  • A variable has a type, a name and a value: int age = 35;.
  • The type decides the size and the range: an int is 4 bytes and stops at about 2.1 billion; long is 4 bytes on Windows but 8 on Linux and macOS.
  • Initialize every variable; reading one that has no value is a bug.
  • Watch the number traps: integer division drops the remainder, values that don't fit wrap around without any warning, and decimals like 0.1 + 0.2 aren't exact.
  • Braces { } catch narrowing, auto deduces the type, and const locks a value.
  • Pick descriptive names that follow the rules: letters, digits and underscores, never starting with a digit.

Next, use your variables to make decisions with the if statement, repeat work with the for loop, and store many values at once with C++ arrays.

Thanks for reading!

Know someone who would enjoy this? Send it their way.

More articles

Read next

Comments

Be kind and specific. Comments with links from new accounts appear after a moderator reviews them.