Skip to content

2. Variables & Data Types

What & why

A variable is a named piece of memory that holds a value. Unlike Python or JavaScript, C++ is statically typed: every variable has a type fixed at compile time, and the compiler uses it to catch mistakes and to decide how much memory to reserve and which machine instructions to use.

Declaring variables

int count = 0;              // whole number
double price = 9.99;        // floating-point number
char grade = 'A';           // single character (single quotes!)
bool is_ready = true;       // true or false
std::string name = "Ada";   // text (needs #include <string>)

The pattern is type name = initial_value;.

The fundamental types

Type Holds Typical size Example
int whole numbers 4 bytes (≈ ±2.1 billion) 42
long long bigger whole numbers 8 bytes 9'000'000'000LL
unsigned int non-negative whole numbers 4 bytes 42u
double real numbers, ~15 digits precision 8 bytes 3.14159
float real numbers, ~7 digits precision 4 bytes 3.14f
char one byte / character 1 byte 'x'
bool truth value 1 byte true

std::string is not built in — it's a class from the standard library — but you'll use it like one.

Check sizes on your machine:

#include <iostream>

int main() {
    std::cout << "int:    " << sizeof(int) << " bytes\n";
    std::cout << "double: " << sizeof(double) << " bytes\n";
    std::cout << "char:   " << sizeof(char) << " bytes\n";
}

Need an exact width? #include <cstdint> gives you std::int32_t, std::uint8_t, std::int64_t, etc.

Initialization: always give a value

int a;          // UNINITIALIZED: holds garbage. Reading it is undefined behavior.
int b = 5;      // copy initialization
int c{5};       // brace (uniform) initialization — preferred in modern C++
int d{};        // value-initialized to 0

Brace initialization refuses narrowing conversions, which catches bugs:

int x = 3.9;    // compiles, silently becomes 3 (warning with -Wall, maybe)
int y{3.9};     // compile ERROR: narrowing conversion from double to int

Rule of thumb: initialize every variable when you declare it, and prefer {}.

auto: let the compiler deduce the type

auto n = 10;          // int
auto pi = 3.14;       // double
auto letter = 'q';    // char
auto s = std::string{"hi"};  // std::string
auto t = "hi";        // const char* — NOT std::string! (a C-style string)

auto is great for long type names; don't use it when the type is important to a reader.

const and constexpr

const double tax_rate = 0.08;         // can't be changed after initialization
constexpr int days_per_week = 7;      // must be known at compile time

tax_rate = 0.1;   // compile ERROR: assignment of read-only variable

Make things const by default. It documents intent and lets the compiler stop accidental changes.

Type conversion

Some conversions happen automatically (implicit):

int i = 7;
double d = i;        // 7.0 — safe, widening

Others you ask for (explicit) with static_cast:

double avg = 7.8;
int rounded_down = static_cast<int>(avg);   // 7 — truncates toward zero

Converting strings ↔ numbers:

#include <string>

int n = std::stoi("42");            // string → int
double x = std::stod("3.5");        // string → double
std::string s = std::to_string(99); // number → string

std::stoi("abc") throws an exception — we'll handle those in lesson 11.

Scope

A variable exists from its declaration to the closing } of the block that contains it:

int main() {
    int outer = 1;
    {
        int inner = 2;
        std::cout << outer + inner << '\n';   // fine
    }
    std::cout << inner << '\n';   // ERROR: 'inner' was not declared in this scope
}

Declare variables as close as possible to where they're first used.

Naming

  • Letters, digits, and _; can't start with a digit; case-sensitive.
  • Can't be a keyword (int, class, return, ...).
  • Pick a style and be consistent: snake_case (the standard library) or camelCase.
  • Avoid names starting with _ followed by a capital, or containing __ — they're reserved.

Common mistakes

  • Using an uninitialized variable — the program may "work" today and print garbage tomorrow.
  • char c = "A"; — double quotes make a string; characters use single quotes 'A'.
  • auto s = "hi"; expecting a std::string — it's a const char*.
  • Integer overflow — int big = 3'000'000'000; doesn't fit in an int.
  • Shadowing — declaring a new variable with the same name in an inner scope hides the outer one. -Wshadow warns about it.

Exercises

  1. Declare one variable of each fundamental type, print them all, and print sizeof each.
  2. Write a program that reads a temperature in Celsius as a double and prints Fahrenheit (F = C * 9 / 5 + 32).
  3. Try int x{3.9};. Read the error. Fix it with static_cast.
  4. Read a number as a std::string with std::cin, convert it with std::stoi, double it, and print it.
  5. Remove the initializer from an int and print it. Compile with -Wall -Wextra. What does the compiler say? What does the program print?

Recap

  • Every variable has a fixed type: int, double, char, bool, std::string, ...
  • Always initialize; prefer brace initialization int x{5}; because it blocks narrowing.
  • auto deduces the type from the initializer.
  • const / constexpr make values read-only — use them by default.
  • static_cast<T>(x) for explicit conversions; std::stoi / std::to_string for text.
  • Variables live until the end of their enclosing { } block.

Next: Numbers & math →