Skip to content
VLSI Mentor

C++ Part I · Module 4 · Expressions and Operators

Expressions and Statements

Module 3 was about what values are. This module is about what you do with them, and it starts by naming the thing you have been writing all along without a word about it. Every piece of C++ that produces a value is an expression — a literal, a variable name, a sum, a comparison, a function call. Every complete instruction is a statement. Those two are not the same, they nest inside each other in ways that matter, and the distinction is what makes the next nine chapters readable rather than a list of symbols. One idea in particular carries the whole module: an expression does not merely produce a value, it produces a value of a type, and the type is frequently not the one you would guess from looking at the operands. That sentence will explain a surprising amount of C++ behaviour over the next few chapters, including two things Module 3 deferred and promised to come back to.

Foundation12 min readC++Part IFundamentalsExpressions

Part I · Module 4 · Chapter 4.1 · Expressions and Statements

1. What an Expression Is

An expression is a piece of code that produces a value.

That is the whole definition, and it covers more than people expect. All of these are expressions:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    19                        // a literal
    cycle_count               // a variable name
    cycle_count + 4           // a sum
    cycle_count > 10          // a comparison
    cycles_for(8)             // a function call

Each one produces a value. The literal 19 produces 19. The name cycle_count produces the value currently held in that object. The sum produces the result of adding. The comparison produces true or false. The call produces whatever the function returns.

Expressions nest. cycle_count + 4 contains two smaller expressions, cycle_count and 4, and uses their values to produce a third. Almost every line of C++ you write is a small tree of expressions, and the operators are what join them.

2. Every Expression Has a Type

Here is the idea that matters most in this module.

Chapter 3.1 §5 said a type decides how bits are interpreted and what operations are allowed. That was about objects. It applies to expressions too:

An expression produces a value of a type. The type is decided when the program is compiled, from the types of the parts.

So cycle_count + 4 does not merely produce "the number 23". It produces an int whose value is 23. And cycle_count > 10 produces a bool.

This sounds like a technicality and it is the opposite. Over the next few chapters you will meet:

  • Two char values added together, producing something that is not a char (Chapter 4.2).
  • A signed and an unsigned value compared, where one is converted before the comparison and the answer surprises everyone (Chapter 4.4 — the explanation Chapter 3.3 §7 promised).
  • Values converted silently on their way into an operator, before any arithmetic happens at all (Chapter 4.10).

Every one of those is a question about the type of an expression rather than its value. Asking "what type is this?" alongside "what value is this?" is the habit this module is trying to build.

3. Some Expressions Also Have an Effect

Most expressions just produce a value and change nothing. Some do something as well:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    cycle_count = 19          // produces a value AND changes the object
    ++cycle_count             // produces a value AND changes the object
    std::cout << "hi"         // produces a value AND writes to the console

A change like that — something observable beyond producing the value — is called a side effect.

This is worth naming early because it is the source of two things later in the module. Chapter 4.5's short-circuit rule is about a side effect that may never happen, and Chapter 4.9's evaluation-order rules are about when side effects take place relative to each other. Neither makes sense without the word.

Notice that cycle_count = 19 is in that list. Assignment is an expression — it produces a value, not only an effect. That surprises people, and Chapter 4.6 shows what it is good for and what it costs.

4. What a Statement Is

A statement is a complete instruction. Statements are the things that run, one after another.

The simplest kind is an expression statement: an expression followed by a semicolon.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    cycle_count = 19;         // expression statement
    cycles_for(8);            // expression statement

That semicolon is doing real work. It turns "a thing that produces a value" into "a thing the program does".

Chapter 2.1 §4 introduced statements and the semicolon from the other direction, as punctuation that ends an instruction. Now the relationship is precise:

Produces a valueCan stand alone as an instruction
ExpressionYesNo
StatementNot necessarilyYes

An expression is a part of a statement. The expression cycle_count + 4 cannot be a line of your program on its own; cycle_count = cycle_count + 4; can.

Other kinds of statement exist — declarations are statements, and Module 5 introduces the ones that control what runs next. For this module, expression statements are nearly all you need.

5. What Happens to a Value You Ignore

If an expression produces a value and the statement does not use it, the value is simply discarded. This is legal:

Azvya Education Pvt. Ltd.VLSI Mentor
exprstmt.cpp — complete program. One line here is pointless.
#include <iostream>

static int cycles_for(int transfers) { return 3 + transfers * 2; }

int main()
{
    int total = 0;

    19;                       // an expression statement that does nothing
    total = cycles_for(8);    // expression with an effect, used as a statement
    cycles_for(4);            // call is an expression; its value is discarded

    std::cout << "total = " << total << '\n';
    return 0;
}

Expected output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
total = 19

Three things to read out of that.

19; is a valid statement. It produces 19, nothing uses it, the program carries on. It is useless, and the compiler says so:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
exprstmt.cpp:9:5: warning: expression result unused [-Wunused-value]

Chapter 2.3 §5 argued that warnings are the compiler telling you about code that is legal and probably wrong. This is a clean example.

cycles_for(4); is not useless in the same way. Its value is discarded too, but it was still called — and if a function has a side effect, calling it for that effect and ignoring its value is a perfectly normal thing to do. The compiler does not warn here, because it cannot know whether you wanted the effect.

total is 19, not 11. cycles_for(8) is 3 + 16; the call on the next line changed nothing because its result went nowhere.

6. One Thing This Chapter Deliberately Leaves Out

C++ classifies expressions further than "produces a value of a type". There is a second property — roughly, whether the expression names an object that will still exist afterwards, or produces a temporary value that will not.

That classification has a name and a precise set of rules, and this chapter is not going to teach them. The reason is sequencing rather than difficulty: the classification exists to support a feature you have not met, and learning the taxonomy before the thing it is for means memorising vocabulary with nothing to attach it to.

Chapter 13.6 teaches it, immediately before the feature that needs it. Until then, "an expression produces a value of a type" is a complete and correct working model for everything in this module.

7. Mistakes Worth Making Once

Thinking about an expression's value and not its type. The type decides what conversions happen, what the result can hold, and sometimes what the value even is. The next three chapters are mostly this mistake in different costumes.

Assuming a statement and an expression are the same thing. An expression is a part; a statement is a whole instruction. cycle_count + 4 is not something your program can do.

Forgetting that assignment produces a value. It does, which is why a = b = c works and why if (x = 5) compiles. Chapter 4.6.

Ignoring an unused-value warning. 19; is harmless. The same warning on a line you believed was doing something is a defect report — frequently a function call where you forgot to use the result.

Reading "side effect" as "something bad". It just means an observable change beyond producing a value. Writing output is a side effect. The word is descriptive, not a judgement.

8. What You Should Be Able to Say Now

  1. An expression is a piece of code that produces a value. Literals, variable names, arithmetic, comparisons and function calls are all expressions.
  2. Expressions nest: an operator joins smaller expressions into a larger one.
  3. An expression produces a value of a type, and the type is determined at compile time from the types of the parts.
  4. Asking "what type is this expression?" is the habit this module exists to build.
  5. Some expressions also have a side effect — an observable change beyond producing a value. Assignment, increment and output all do.
  6. A statement is a complete instruction. An expression statement is an expression plus a semicolon.
  7. An expression cannot stand alone as a line of your program; a statement can.
  8. A produced value that nothing uses is discarded. 19; is legal and warns with -Wunused-value.
  9. A call whose result is discarded does not warn, because the compiler cannot know whether you wanted a side effect.
  10. C++ classifies expressions further than this chapter does. Chapter 13.6 owns that, and "a value of a type" is complete for this module.

9. Exercises

Exercise 1 — expression or statement? For each, say which it is: (a) cycle_count (b) cycle_count; (c) cycle_count + 4 (d) int cycle_count = 19; (e) cycles_for(8).

Exercise 2 — name the type. Using what Module 3 taught, give the type of each expression where cycle_count is an int and ratio is a double: (a) cycle_count (b) cycle_count > 10 (c) ratio (d) 19.

Exercise 3 — which have side effects? (a) cycle_count + 4 (b) cycle_count = 4 (c) cycle_count > 4 (d) ++cycle_count (e) std::cout << cycle_count.

Exercise 4 — explain the warning. The compiler warned about 19; but not about cycles_for(4);. Explain the difference in terms of what the compiler can and cannot know.

Exercise 5 — predict the total. In §5's program, total printed 19. Say what each of the three statements contributed, and what total would be if the second and third lines were swapped.

Exercise 6 — find the real bug. You get warning: expression result unused on this line: packet.checksum();. The function has no side effects. Explain what the author probably meant to write, and why the warning found a genuine defect rather than a harmless oddity.

Exercise 7 — the value you always throw away. std::cout << total; is an expression statement. Using §3 and §5, name its effect, say what value the expression produces, and explain why no "result unused" warning appears here even though that value is discarded — where Exercise 4's 19; did warn.

10. Summary

  • An expression produces a value. Literals, names, arithmetic, comparisons and calls all qualify, and they nest.
  • An expression produces a value of a type — decided at compile time, and frequently not the type you would guess. This is the module's central thread.
  • Some expressions also have a side effect: an observable change beyond the value. Assignment is one, which is why assignment is an expression.
  • A statement is a complete instruction; an expression statement is an expression plus a semicolon.
  • An expression cannot stand alone; a statement can.
  • An unused value is discarded, and -Wunused-value tells you when that was probably a mistake.
  • A further classification of expressions exists and belongs to Chapter 13.6, where the feature that needs it arrives.

With expressions named, the operators can be taken one group at a time. The first group is the one everybody assumes needs no explanation — add, subtract, multiply, divide — and it contains a genuine surprise. Add two values of a small type together and the result is not of that type; Chapter 3.3 needed a cast for exactly this reason and deferred the explanation, and Chapter 3.4 ran into it again. Chapter 4.2 is where that debt is paid, and the answer is a rule that applies to every arithmetic expression you will ever write.

Standards & specifications

Governing standard
ISO/IEC 14882 (the C++ language standard)(opens ISO/IEC (JTC 1/SC 22/WG 21) in a new tab)

Defines the C++ language and its standard library — the type system and templates, the object model and object lifetime, initialisation and overload resolution, the memory model and the containers, algorithms and utilities the library provides. Like C's standard it is a FAMILY of editions (C++98/03, C++11, C++14, C++17, C++20, C++23) and the editions are NOT interchangeable: a feature this curriculum relies on may be unavailable, differently specified or merely proposed in another edition, so each lesson names the edition its claim depends on. It does not define any foreign-function interface to a hardware description language, nor what a compiler actually emits, nor platform behaviour on real silicon — those are the implementation's and the device's contracts. Working drafts published by WG21 are freely available and are what most engineers read day to day; a draft is not the published standard.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the C++ Programming curriculum.