Variables, Initialization, and auto
A variable is a named storage location in memory that holds a value of a specific type. In C++, every variable must be declared before use — you tell the compiler what type it is, and the compiler reserves the right amount of memory and knows how to interpret the bits stored there. The distinction between declaration (telling the compiler a variable exists), definition (actually allocating the storage), and initialization(giving the variable its first value) matters in C++ in ways it does not in Python.
Declaring and initializing variables
In C++, a variable declaration specifies the type first, then the name. You can optionally provide an initial value on the same line (an initializer). Providing an initializer is almost always the right thing to do: an uninitialized variable contains whatever garbage was last written to that memory location — reading it is undefined behavior.
// Declaration without initialization — dangerous!
int count; // value is undefined — reading count is UB
// Initialization at declaration — always prefer this
int count = 0; // copy initialization
int width(100); // direct initialization
int height{200}; // brace (uniform) initialization ← preferred in modern C++
double price = 9.99;
bool isOpen = true;
char grade = 'A';The three initialization syntaxes are mostly equivalent for built-in types, but brace initialization (curly braces) has one important advantage: it prevents narrowing conversions. A narrowing conversion is one where information can be lost — for example, initializing an int from a double. Brace initialization makes this a compile error; the other forms silently truncate.
double pi = 3.14;
int a = pi; // compiles — silently truncates to 3
int b(pi); // compiles — silently truncates to 3
int c{pi}; // compile ERROR — narrowing conversion not allowed
// This is a feature: the compiler tells you before you lose dataType inference with auto
C++11 introduced the auto keyword for type inference. When you write auto x = expression;, the compiler deduces the type of x from the type of the expression on the right-hand side. The type is still fixed at compile time — autois not like Python's dynamic typing; it is purely a shorthand that avoids repeating a type you already specified on the right.
auto count = 0; // int auto price = 9.99; // double auto letter = 'A'; // char auto flag = true; // bool auto big = 5LL; // long long — the 'LL' suffix forces long long // auto is most useful when the type is verbose or obvious from context: auto it = myVector.begin(); // saves writing std::vector<int>::iterator auto result = compute(); // type deduced from compute()'s return type // auto does NOT allow changing type later — it is still static typing: auto x = 42; // x is int x = "hello"; // compile error: cannot assign const char* to int
Style note: use auto when the type is obvious from the right-hand side or when it is long and repetitive. Avoid it for simple numeric variables where the type choice matters to a reader (e.g., whether something is int vs double).
Constants — const and constexpr
A variable declared const cannot be modified after initialization. The compiler enforces this: any attempt to assign to a const variable is a compile error. Use const for values that should not change — it documents intent and lets the compiler catch accidental modifications.
const double TAX_RATE = 0.08; const int MAX_RETRIES = 3; TAX_RATE = 0.09; // compile error: cannot assign to const // constexpr — value must be known at compile time constexpr int BOARD_SIZE = 8; // always 8; evaluated by compiler constexpr double PI = 3.14159265; // compiler constant // Prefer constexpr over const when the value is truly compile-time constant: constexpr int SQUARES = BOARD_SIZE * BOARD_SIZE; // 64 — computed at compile time
constexpr is stronger than const: it guarantees the value is computed at compile time and can be used in places that require compile-time constants (array sizes, template arguments). For named constants, prefer constexpr.
Scope — where variables live
The scope of a variable is the region of the program where it can be used. In C++, scope is determined by curly braces: a variable declared inside a block { } only exists until the closing brace. Variables declared at the top level of a function (in its outermost block) exist for the entire function. Variables declared inside nested blocks only exist inside those blocks.
void example()
{
int x = 10; // x lives for the whole function
{
int y = 20; // y only lives inside this inner block
x = x + y; // fine — x is visible here
}
// y = 30; // compile error: y no longer exists here
if (x > 5)
{
int z = x * 2; // z lives until the closing brace of the if
std::cout << z;
}
// z is gone here
}An inner scope variable can shadow an outer scope variable of the same name — inside the inner scope, the name refers to the inner variable. This compiles but is confusing; most compilers warn about it with -Wshadow.
Naming variables well
C++ identifiers (names for variables, functions, types) can contain letters, digits, and underscores, and must not start with a digit. Names that start with an underscore followed by an uppercase letter (like _Foo) or contain double underscores (__x) are reserved for the compiler and standard library — do not use them.
Naming conventions in C++
camelCasestudentCount, firstNameLocal variables and function parameters — most common for variablessnake_casestudent_count, first_nameAlso common for variables and functions, especially in the standard libraryUPPER_SNAKE_CASEMAX_SIZE, PICompile-time constants (constexpr / const at namespace scope)PascalCaseStudentRecord, HttpClientClass and struct namesKey rules to remember
Always initialize variables at declaration
Uninitialized variables hold garbage. Reading them is undefined behavior. There is no runtime check — you get a crash or wrong output, not a Python-style NameError.
Prefer brace initialization {value} over = value
Brace initialization rejects narrowing conversions at compile time, which catches common mistakes like accidentally truncating a double to an int.
Use auto when the type is obvious from the right-hand side
auto is cleaner for iterators, factory return types, and complex template types. For simple numeric variables, writing the type explicitly aids readability.
Use const for values that should not change, constexpr for compile-time constants
const documents your intent and lets the compiler catch mutations. constexpr additionally guarantees the value is computed at compile time.
Variables only exist within the enclosing { } block
C++ scope is block-scoped. Avoid declaring a variable before you are ready to initialize it — defer the declaration to the point where you have the value.