The operators in the productions below expect the first operand to identify an object, and modify its value.
- unary-expression
-
++ unary-expression
Side-effects -
-- unary-expression
Side-effects - postfix-expression
-
postfix-expression ++
Side-effects -
postfix-expression --
Side-effects - assignment-expression
unary-expression assignment-operator assignment-expression
- assignment-operator
=
*=
/=
%=
+=
-=
<<=
>>=
&=
^=
|=
Such a modification is called a side-effect, and it is the only way to modify the value of an object. An expression that identifies an object is an l-value, pronounced ‘el value’. If the object can be modified using that expression, the expression is a modifiable l-value. Operators with side-effects require the first operand to be a modifiable l-value.
Expressions are often useful for their side-effects alone, with the result being discarded. This happens most often in expression statements:
(Other places include the initializer and increment of a
for
loop, and
the left operand of the comma operator (see expression).)
Given the following declarations:
int x; int *xp = &x; int arr[10] = { 0 }; // All zero
…here are a dozen or so expression statements with their side-effects explained:
// The l-value is a simple variable name. x = 10; // Setx
to10
. x++; // Setx
to11
. x--; // Setx
to10
. ++x; // Setx
to11
. --x; // Setx
to10
. // The l-value identifies an object by dereferencing its address. *xp = 6; // Setx
to6
. (*xp)++; // Setx
to7
. --*xp; // Setx
to6
. // The l-value identifies an object in an array. arr[3] = 10; // Setarr[3]
to10
. ++arr[4]; // Setarr[4]
to1
. arr[5]--; // Setarr[5]
to-1
. arr[x - 2] -= 3; // Setarr[4]
to-2
.
The operator ++
increments the
object by one, while --
decrements by
one. The simple assignment operator =
assigns the value of its right operand to the object
identified by the left operand. The operator -=
decrements the object on the left by the value
on the right. Other operators behave similarly, i.e., x
op= y
is equivalent to
x = (x) op
(y)
.
As expressions, all these operators yield a value,
although it can be, and often is, ignored. The assignment
operators yield the value that is assigned, permitting
expressions such as x = y = z
, which
assigns the value of z
to both
x
and y
. The
operators ++
and --
yield a value depending on whether the operand
is to the left or right of the operator. ++x
yields the new value of x
; x++
yields the previous
value of x
.
Unmodifiable l-values are array names, and l-values of
types qualified with
const
:
char arr[10]; const char *p = arr; char *q; arr = q; // Error; arrays cannot be moved. *(arr + 4) = 'c'; // Okay;arr + 4
has typechar *
. arr[4] = 'c'; // Okay; same as above. *(p + 4) = 'c'; // Error;p + 4
has typeconst char *
. p[4] = 'c'; // Error; same as above.
Although *(p + 4)
identifies an
object, and is therefore an l-value, p +
4
has type const char *
, so
*(p + 4)
must have type const char
, and that
makes it unmodifiable.
Some expressions are used to invoke functions, and follow this grammar:
Depending on what it has access to, the invoked function may produce side-effects of its own.