Expressions and Evaluation
This chapter defines what an expression is in C, how expressions are evaluated to produce values, and the crucial distinction between an expression's *value* and its *side effects*. Every operator chapter that follows builds on this.
Why This Matters
C is an expression-oriented language. Assignments, function calls, arithmetic, and even some control-flow constructs are expressions. A precise model of "expression" and "evaluation" is required before sequencing, precedence, and undefined behavior make sense.
Prerequisites
c.core.2— objects, values, and the abstract machine.
Core Concept
What an expression is
An expression is a sequence of operators and operands that specifies computation of a value, designates an object or function, generates side effects, or does a combination of these.
Expressions are built from:
- Primary expressions: identifiers, constants, string literals, parenthesized
expressions, and (C99) compound literals.
- Operators: unary, binary, and ternary operators combine subexpressions.
- Function calls:
f(args).
Evaluation
Evaluating an expression produces a value or a designation, and may cause side effects (modifying an object, calling a function, doing I/O).
There is a critical distinction:
- An expression has a value (the result of evaluation).
- An expression may have side effects (changes to the state of the abstract
machine).
Example: in x = 5, the value of the whole expression is 5 (the assigned value), and the side effect is that the object x is modified.
lvalue expressions
An lvalue is an expression that potentially designates an object. Most object-typed expressions are lvalues (variable names, *p, array element access, struct member access). A modifiable lvalue is an lvalue that is not an array, not an incomplete type, and not const-qualified.
Syntax
Primary expressions:
identifier
constant /* 42, 3.14, 'a' */
string-literal /* "hello" */
( expression )
compound-literal /* (int){5} — C99 */
Compound expressions use operators, which are covered in c.core.9 through c.core.13.
Examples
Values vs. side effects
int x = 1;
int y = (x = 5); /* value of (x = 5) is 5; side effect: x becomes 5 */
/* now x == 5, y == 5 */
Expressions as statements
In C, an expression followed by a semicolon is an expression statement:
x = 5; /* expression statement */
f(); /* function call expression statement */
x++; /* increment expression statement */
lvalues vs. non-lvalues
int a = 1;
int *p = &a; /* a is an lvalue; &a is valid */
/* & (a + 1) ERROR: (a + 1) is not an lvalue */
How It Works
The abstract machine evaluates expressions by combining the values of subexpressions according to operator semantics. In the absence of side effects, evaluation order for most operators is unspecified — only the *value computation* is constrained. This is the heart of c.ops.sequencing.
Two concepts govern evaluation:
- Value computation: computing the result value.
- Side effects: modifications and function calls.
The standard's *sequencing* rules say which evaluations happen before which. This is covered fully in c.core.13.
Variations
Full expressions and sequencing
A full expression is an expression that is not part of another expression (for example, an expression statement or the controlling expression of an if). There is a sequence point after each full expression.
Discarded-value expressions
Some expressions are evaluated only for their side effects; their value is discarded. An expression statement x++; is an example.
Common Mistakes
- Confusing "evaluation order" with "precedence." Precedence determines how
expressions are *parsed*, not the order subexpressions are *evaluated*.
- Assuming side effects happen in a particular order when they are unsequenced
(this is UB).
- Writing
& (a + 1)— the result of+is not an lvalue.
Undefined Behavior
- Two unsequenced side effects on the same scalar object, or an unsequenced
side effect and a value computation using the same scalar object, is undefined behavior (c.ops.sequencing). VERIFIED
- Reading an uninitialized automatic object is often UB (
c.obj.indeterminate).
Portability
- The abstract machine is standard; specific evaluation strategies are not.
This means different compilers may legitimately produce different results for expressions that depend on unspecified evaluation order.
Under the Hood
The compiler parses an expression into an abstract syntax tree (AST), then lowers it to intermediate representation and machine code. Optimization transforms the tree/IR aggressively but must preserve observable behavior. The AST is also the subject of c.compiler.1.
Practical Usage
- Write expressions that are correct regardless of evaluation order (avoid
unsequenced modifications).
- Break complex expressions into statements for readability and to make
sequencing explicit.
Exercises
1. For each of x = 5, f(), *p, a + b, and "str", state whether it is an lvalue and whether it has side effects. 2. Write a program that demonstrates the value of an assignment expression by assigning the result to another variable. 3. Explain why &(a + 1) is illegal.
Deep Challenge
Using the C standard's definitions of "expression," "evaluation," "lvalue," and "side effect," explain precisely what is wrong with:
int i = 0;
int j = (i = 1) + (i = 2);
Identify whether this is undefined behavior, unspecified behavior, or implementation-defined, and cite the relevant rule category.
Related Concepts
c.core.9–c.core.13— operators and sequencing.c.obj.indeterminate— indeterminate values.c.compiler.1— expressions as ASTs.
References
- ISO/IEC 9899:2018 §6.5 (expressions), §6.3.2.1 (lvalues, values, and
function designators).
Verification
- The definition of expression and lvalue is standard.
VERIFIED - Assignment is an expression whose value is the assigned value.
VERIFIED - Unsequenced modification UB is standard.
VERIFIED - No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] Expression definition
- [ ] Evaluation and side effects
- [ ] lvalue vs. non-lvalue
- [ ] Expression statements
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.lang.expr | 0 | 5 |