C Part I · Module 3 · Expressions and Operators
What an Expression Is
Module 2 described a single object completely — what it is, where it lives, what it holds and how that is recorded — and then admitted that none of its programs actually did anything with those objects except print them. This module supplies the missing half. Before any operator arrives, though, one idea is worth establishing on its own, because everything in the next eleven chapters is a special case of it: an expression is something that produces a value, that value has a type, and some expressions additionally change something while they are at it. Those are two separable properties, and almost every confusing thing in C's operators becomes straightforward once you are in the habit of asking about both.
Foundation10 min readCPart IFundamentalsExpressions
Part I · Module 3 · Chapter 3.1 · What an Expression Is
1. The Gap Module 2 Left
Every program so far has done one of two things with a value: stored it, or printed it. None of them has worked anything out.
int width = 4;
int height = 3;Two objects, two values, and no way yet to say the area is width times height. Computing a new value from existing ones is what this module is about, and the thing that does it is called an expression.
2. An Expression Produces a Value
Here is the smallest useful definition, and it is worth reading twice:
An expression is something that produces a value.
That is nearly everything. A literal is an expression — 3 produces the value 3. A name is an expression — width produces whatever the object named width currently holds. And combining them with an operator produces a new value:
#include <stdio.h>
int main(void)
{
int width = 4;
int height = 3;
int area = width * height;
printf("area is %d\n", area);
printf("width * height is %d\n", width * height);
printf("width is still %d\n", width);
return 0;
}Output:
area is 12
width * height is 12
width is still 4Take width * height apart, because it is the shape of every expression you will meet:
widthis an expression. It produces 4 — the value currently held by the object of that name.heightis an expression. It produces 3.*is an operator. The things it is applied to are its operands.width * heightis an expression built from two smaller ones. It produces 12.
The third printf line is there deliberately. Computing width * height did not change width. The expression read the values out of the two objects and produced a new value, exactly as Chapter 2.1's int limit = count; read a value without linking anything. Reading an object and changing one are different acts, and most expressions only read.
3. Every Expression Also Has a Type
Here is the half that beginners are almost never told, and it is the reason this module exists in the form it does:
An expression does not merely produce a value. It produces a value of a type.
width * height did not just produce 12. It produced 12 as an int, because both its operands were int. That type is decided by rules in the language, before the program runs, and it is as real a property of the expression as the value is.
Why does that matter when the answer was obviously 12 either way? Because the type of an expression decides what the result is allowed to be, and in three cases coming up shortly it produces answers that look wrong:
- Chapter 3.3 —
7 / 2produces 3, not 3.5, because the expression's type isintand aninthas nowhere to put the half. - Chapter 3.5 — a comparison can give the opposite of what you expect when its operands have different signedness. Chapter 2.6 showed you this happening; the reason is a question about types.
- Chapter 3.12 — the type of an expression is sometimes not the type of either operand, because C converts them first.
In every one of those, the value surprises you and the type explains it. So the habit worth forming from this chapter is to ask two questions about any expression rather than one:
What value does this produce, and what type is that value?
4. Some Expressions Also Change Something
Everything in §2 read values and produced a new one. Some expressions do something else as well:
#include <stdio.h>
int main(void)
{
int count = 5;
count = 9;
printf("count is %d\n", count);
return 0;
}Output:
count is 9count = 9 produces a value, like any expression — but it also changes the object named count. That change is called a side effect: something an expression does besides producing its value.
The distinction is worth naming now, even though its consequences arrive later, because three separate chapters depend on it:
- Chapter 3.4 shows that assignment really is an expression with a value, not a special kind of statement.
- Chapter 3.7 introduces two operators whose entire purpose is a side effect.
- Chapter 3.11 asks when side effects happen relative to each other — and the answer is less reassuring than you would guess.
For now, the classification is enough: most expressions only produce a value; some also change something.
5. Expressions and Statements Are Not the Same Thing
One more distinction, because the two words are easy to blur.
Chapter 1.4 introduced a statement as one step for the program to carry out, ended by a semicolon. An expression is something that produces a value. They are different categories, and an expression becomes a statement when you put a semicolon after it:
width * height; /* a valid statement — and a useless one */That is legal C. It computes 12 and then throws it away, because nothing was done with the result. Compile it with the options from Chapter 1.5 and the compiler will point out that the line has no effect — a good example of a warning catching something that is not an error but is almost certainly a mistake.
By contrast:
count = 9; /* an expression statement that does something */is the same shape — an expression with a semicolon — but useful, because this expression has the side effect from §4.
That is the practical version of the distinction: an expression statement is worth writing when the expression has an effect, and is a mistake when it does not.
6. What You Should Be Able to Say Now
- An expression is something that produces a value. Literals and names are expressions; so is any combination of them with operators.
- An operator is applied to operands, which are themselves expressions.
- Computing with an object reads it. It does not change it.
- An expression produces a value of a type, and the type is decided by the language's rules.
- Asking what value and what type is the habit this module is built on — the type is what explains the results that look wrong.
- Some expressions also have a side effect: they change something besides producing a value.
- An expression and a statement are different categories; an expression followed by a semicolon is an expression statement.
- An expression statement with no effect is legal and almost always a mistake — and the compiler will say so.
7. Exercise
Exercise 1 — find the expressions. In int area = width * height;, list every expression you can identify, including the smallest ones.
Exercise 2 — predict, then run. What does this print? Say what a holds at the end, and why.
#include <stdio.h>
int main(void)
{
int a = 6;
int b = 2;
printf("%d\n", a * b);
printf("%d\n", a);
return 0;
}Exercise 3 — classify. For each, say whether it has a side effect:
(a) width * height
(b) count = 9
(c) width
(d) 3
Exercise 4 — explain the warning. You write width * height; as a statement and the compiler warns that it has no effect. Explain what the compiler noticed, and why the same warning does not appear for count = 9;.
Exercise 5 — ask both questions. For width * height where both are int, state the value and the type. Then say which of the two you would have to think about if width were a double instead — you are not expected to know the answer yet, only which question changes.
8. Summary
- An expression produces a value. Literals, names, and combinations built with operators are all expressions.
- Operators apply to operands, and operands are themselves expressions — which is why expressions nest.
- Reading an object does not change it.
width * heightleaves both objects alone. - Every expression has a type as well as a value, decided by the language before the program runs.
- Ask both questions. The value is usually obvious; the type is what explains the results that are not.
- Some expressions have side effects — they change something as well as producing a value.
- Expressions and statements are different. An expression statement with no effect is legal, useless, and reported by the compiler.
You now have the shape of an expression and the two questions worth asking about one. The next chapter fills in the operators that do arithmetic — and the first of them produces a result that is entirely correct and surprises almost everyone.
Standards & specifications
- Governing standard
- ISO/IEC 9899 (the C language standard)(opens ISO/IEC (JTC 1/SC 22/WG 14) in a new tab)
Defines the C language itself — the type system, expression semantics, object model, storage duration and the behaviour categories a conforming implementation must honour. It is a FAMILY of editions (C89/C90, C99, C11, C17, C23) and this curriculum names the edition wherever a claim depends on one. It does not define any foreign-function interface to a hardware description language, and it does not define platform behaviour: what a memory-mapped access does on real silicon is the implementation's and the device's contract, not this standard's. Working drafts published by WG14 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.
