Concepts / Initializing Variables

Initializing Variables

Auto lets the compiler deduce a variable's type from its initializer, so the form is auto variable_name = initializer;.

  • Programming
Interactive lab

Try it: Assignment, Statement by Statement

How an assignment statement evaluates its right side first (using the names' current values) and only then binds the name on the left: updating x = x + 1, augmented operators like += and //=, chained a = b = 0, simultaneous a, b = b, a + b versus one-at-a-time assignment, rebinding a name to a value of another type, the NameError for a name that was never assigned, and the SyntaxError for a name that breaks the naming rules.

How it works

  1. Before anything runs, Python compiles the whole program; a name that breaks the rules (starts with a digit, contains a space or a dash, or is a keyword) is a SyntaxError and no line runs at all.
  2. For each assignment, evaluate the complete right side first, looking every name up in the variable table as it is now.
  3. Then bind the name(s) on the left to that value: a new name is added to the table, an existing one is replaced (even by a value of a different type).
  4. x += 5 reads x, applies the operator (+, -, *, //, %, **) and binds the result back to x; a = b = 0 evaluates 0 once and binds a, then b.
  5. a, b = b, a + b builds the whole right-hand tuple from the OLD values before binding a and b; two separate statements would use the new a in the second line.
  6. Reading a name that is not in the table raises NameError and the program stops at that line.

Default run (10 steps): A 4-line program. Python first compiles all of it, then runs it top to bottom. The variable table on the right starts empty. … Program finished. Final table: x = 16.

Simplified: The programs are fixed templates: you choose the template, its numbers, the operator and the variable name, never the code. The lab's own small Python tokenizer and parser (written for the statements these programs use: assignments, print calls, if / elif / else, for, while, def and return) checks the text; nothing is executed as real Python. Every program the lab can show was compiled by CPython 3.12 and the lab reproduces its exception type, message, line, caret columns and the printed report. A small interpreter written for the same statements then runs them (integers of any size, the str/float results these templates produce, and print); each program was also run in CPython 3.12 under sys.settrace, and the lab reproduces the variables after every line, the output and the error.

Educational simulation

Loading the simulation…

Why a Starting Value Can Surprise You

Most beginners assume that when they declare a variable and give it a value, the variable holds exactly the value written in the code. For whole numbers and matching types, that assumption works without trouble. It starts to fail when the value you write does not fit the type you declared. A variable declared as an int that is given a decimal literal such as 7.2 does not simply keep 7.2, and it does not always refuse to compile. The actual behavior is more specific, and this article traces it step by step so you can predict it and guard against it.

The article follows one question throughout: when a value is placed into a variable, what exactly happens to it, and how can a silent change slip past you? To answer that, we first need a precise idea of what initialization is.

What Initialization Does at Declaration

Definition

Initialization assigns a starting value at the moment a variable is declared, and the value you give must suit the declared type.

Declaring and initializing are usually written as a single statement, which is why the two ideas blur together for beginners. The declaration supplies the name and the type. The initialization supplies the first value. Because a value is supplied at the same moment the name is introduced, the practical discipline is to avoid introducing a name until you have a suitable value for it.

cpp
Notes on the code
  • The type name (double, int) comes first and tells the reader what kind of value the variable holds.
  • The equals sign gives the variable its starting value.

Letting the Initializer Choose the Type

Most variable definitions name the type before the variable. Auto offers a different route: you leave the type out and let the value on the right-hand side decide it. The rule is simple to apply once you look at the right-hand side first. Whatever kind of value the initializer produces is the kind of value the variable will be. A literal such as true has a bool type, a character in single quotes has a char type, a whole number like 123 has an int type, and a number with a decimal point such as 1.2 has a double type.

cpp
Notes on the code
  • No type name appears on the left; the type is copied from the right-hand side.
  • The equals sign still does its normal job of giving the variable a starting value.
auto price = 1.2;declarationInitializer 1.2decimal literalType doublefrom the valueprice is doubletype copied to variableread right sidevalue decides typecopied onto variable
How does the compiler move from the right-hand side of auto x = initializer; to the type it gives x?

The same idea reaches beyond literals. Consider a declaration whose initializer is a function call, such as auto z = sqrt(y);. The variable z does not get a type from anything you chose by looking at z. Its type is whatever sqrt(y) returns, and the compiler copies that type onto z. Because the function call is the source of the information, auto is not limited to literal values; it applies to any expression that produces a value.

Why auto Avoids a Conversion at the Start

Conversions are where surprises usually come from. When the type written on the left does not match the type of the value on the right, the value may be changed to fit, and that change may lose information. An auto declaration cannot produce that mismatch at the point of the declaration, because auto does not change the type of the value. The variable's type is the type of the initializer, so the value is stored in a variable of exactly that type. This is the sense in which auto avoids a conversion at the start of the variable's life.

Choosing Between auto and an Explicit Type

The default approach is to use auto when you have no specific reason to mention the type. Auto is the everyday choice, and an explicit type is the exception that needs a justification. Two kinds of justification appear in the source. The first is scope: when a declaration sits in a large scope, naming the type makes the variable's kind visible to a reader who is far from where it was introduced. The second is precision: when the exact range or precision of the value matters, naming the type makes that requirement explicit.

Auto also helps you avoid redundancy and avoid writing long type names. The source notes that this is especially important in generic programming, where the exact type of an object can be hard for the programmer to know and the type names can be quite long. In that setting, auto lets you define the variable without first working out and typing the full name.

Two Notations for Initialization

C++ offers two notations for giving a variable its starting value. The first is the = form, which is the traditional style and dates back to the C language. The second is a universal form based on curly-brace-delimited lists, sometimes called the initializer list form. For ordinary cases, both notations produce the same result. The difference appears when the value does not match the type.

cpp
Notes on the code
  • In every line the variable name comes before the equals sign or the braces, and the value sits inside the braces or after the equals sign.
  • For matching types, a and b hold the same value; the notations only diverge when the value does not fit the type.

The source gives clear guidance on which to prefer. The = form is acceptable and familiar, but when in doubt, use the general braced form. Its most practical benefit is the protection it gives against conversions that lose information, which the next sections examine.

Predicting a Value Stored in an int

What do you think happens?

The declaration int x = 7.2; uses the traditional = notation to initialize an int with a decimal literal. What value does x end up holding?

  • 7.2, kept exactly

  • 7

  • The program refuses to compile

  • 8

Reveal answer
Answer:

7

The 0.2 is the lost portion. The = form applies the conversion implicitly, so the stored value is the valid int 7 and no warning points to the declaration.

Narrowing Conversions and Lost Information

A narrowing conversion, such as double to int, loses information. Narrowing conversions are allowed and are applied implicitly, so they can change a value silently. The language does not stop you from writing them with the = form. The source describes the resulting problems as a price paid for compatibility with C. In practice, the compiler will often accept code whose value has quietly changed, so safety has to come from how you write the initialization.

The lost portion in the prediction above is the 0.2. Nothing in the = form warns you that this happened. The stored value is a valid int, so the program carries on, and any later calculation that depends on the original decimal will be wrong with no error message pointing back to the declaration. The value you see is not the value you wrote, and the mismatch is invisible at the moment it occurs.

3.9double3int, stored0.9discardedassigned to intfractional part lost
When a double like 3.9 is assigned to an int, what value is actually stored, and which part of the original value is lost?

Read the diagram from left to right. The original double value on the left has a whole part and a fractional part. The int on the right keeps only the whole part, so the fractional part is the information that disappears. The same reasoning explains the prediction: 7.2 becomes 7 because the 0.2 is discarded.

Catching Narrowing with the Braced Form

The braced form rejects narrowing conversions, turning a silent value change into a compile-time error that points at the problem. The three initializations below all try to place the same decimal value into an int. Their results differ, and the differences are the reason the braced form is recommended when you are unsure.

Before
int x = 3.9;    // = form: compiles, x silently holds 3

The = form applies the narrowing implicitly and the program continues with a changed value.

After
int x{3.9};     // braced form: compile-time error, the narrowing is rejected

The braced form refuses the narrowing, so the problem is reported before the program runs.

int x = 3.9 or intx{3.9}initializationWhich notation?check the braces= formnarrowing allowedx holds 3silent conversionBraced formnarrowing rejectedCompile-time errorreported at declarationexamineequals signcurly bracesvalue changednarrowing found
When I write int x{3.9} instead of int x = 3.9, what does the compiler do differently, and which path leads to an error versus a silent conversion?

The pattern is that the braced form turns a silent problem into a visible one. The compiler refuses the narrowing rather than applying it. This is the practical reason the source recommends the general braced form whenever you are in doubt about whether a value fits its type.

Mistakes Beginners Make with Initialization

  • Common mistake

    Assuming the variable holds exactly the value written, even when the value does not fit the declared type.

    What it looks like
    int x = 7.2; and then expecting x to hold 7.2.
    Why it fails

    The = form applies a narrowing conversion implicitly, so the 0.2 is discarded.

    Do this instead

    Give the variable a value that matches its type, or use the braced form so the mismatch is reported.

  • Common mistake

    Introducing a name before you have a suitable value for it.

    Why it fails

    Declaring and initializing happen together, so a poor starting value is baked into the variable from the first line.

    Do this instead

    Declare the variable where you have a suitable value to give it.

  • Common mistake

    Using auto when the type of the variable matters for the reader or the required precision.

    Why it fails

    Auto hides the type name, which can obscure intent in a large scope or where exact precision is required.

    Do this instead

    Write the explicit type in those cases, since an explicit type is the exception that needs justification.

  • Common mistake

    Treating the = form and the braced form as always different. For matching types they produce the same result; they diverge only when the value does not fit.

Tracing a Silent Change

MEDIUM

Given the declaration double total = 19.99; followed by int rounded = total; what value does rounded hold, and which part of the original value is lost? Then rewrite the second line using the braced form and state what the compiler does.

Hints
  • Identify the type of total on the right-hand side first.
  • A narrowing conversion keeps the whole part and discards the fractional part.
  • Think about what the braced form does when the value does not fit the type.

Key Takeaways

  • Initialization assigns a starting value at the moment a variable is declared, and the value must suit the declared type.
  • With auto, the initializer's type is copied onto the variable, so auto avoids a type conversion at the declaration; use auto by default and write an explicit type for large scopes or exact precision.
  • A narrowing conversion such as double to int is allowed and applied implicitly, and it discards information such as the fractional part.
  • The braced form rejects narrowing conversions and reports them at compile time, so it is the recommended choice when in doubt.

Related concepts

More concepts starting with I