Assignment Operators and Expressions
Chapter Eighteen
Syllabus topic 2, "Type of operators: Arithmetic operators, relational and logical operators, Increment and Decrement operators, assignment operators, the conditional operator, Assignment operators and expression, Precedence and order of Evaluation Block Structure, Initialization, C Preprocessor"
Pages 85 to 89 of 222
In one line
= stores a value in a variable and is itself an expression whose value is what was stored, and the ten compound forms such as += do an operation and the store in one step.
Plain assignment
variable = expression;The right-hand side is evaluated, converted to the type of the left-hand side, and stored. The left-hand side must be something that can be assigned to: a variable, an array element, a structure member, or the thing a pointer points at. The language's word for that is an lvalue, and the name comes from its position on the left of an assignment.
x = 5; /* fine */
a[i] = 5; /* fine */
5 = x; /* does not compile: 5 is not a place */
x + 1 = 5; /* does not compile: a value, not a place */An assignment is an expression
This is the part of the topic that matters, and it is why MU lists it separately.
The value of x = 5 is 5. An assignment is not a statement that happens to look like one; it is an operator that stores its right operand and then yields that value. Three consequences follow, and all three appear in real C.
1. Chained assignment. a = b = c = 0; works because = groups right to left: c = 0 yields 0, b = that yields 0, a = that yields 0.
#include <stdio.h>
int main(void)
{
int a, b, c;
a = b = c = 7;
printf("a = %d, b = %d, c = %d\n", a, b, c);
printf("the value of the expression (a = 99) is %d\n", (a = 99));
printf("and a is now %d\n", a);
return 0;
}a = 7, b = 7, c = 7
the value of the expression (a = 99) is 99
and a is now 99Chaining assigns the same value to each, after conversion at each step. int i; double d; i = d = 3.7; leaves d as 3.7 and i as 3, not both 3.7.
2. Assignment inside a condition. The idiom every C program uses for reading input:
while ((c = getchar()) != EOF) {
...
}Read that from the inside out: getchar() is called, the result is stored in c, the value of the assignment is that same result, and it is compared against EOF. One line does read, store and test.
The inner brackets are not optional. != binds tighter than =, so while (c = getchar() != EOF) would compare first and store the 1 or 0 into c.
3. The if (x = 5) bug. Chapter 16 met it. This is the reason it compiles: the assignment is a legal expression with the value 5, and 5 is not zero, so the condition is true.
Assignment Operators and Expressions
Where an assignment in a condition is deliberate, put extra brackets round it. That is the convention that tells a reader, and the compiler, that you meant it, and it is what turns off the warning.
The compound assignment operators
| Operator | x op= y means | Example with x = 10 |
|---|---|---|
+= | x = x + (y) | x += 3 leaves 13 |
-= | x = x - (y) | x -= 3 leaves 7 |
*= | x = x * (y) | x *= 3 leaves 30 |
/= | x = x / (y) | x /= 3 leaves 3 |
%= | x = x % (y) | x %= 3 leaves 1 |
Five more exist for the bitwise operators, &=, |=, ^=, <<= and >>=. They work the same way. MU names no bitwise operator on this paper, so they are not taught here; know that they exist.
#include <stdio.h>
int main(void)
{
int x = 10;
printf("x starts at %d\n", x);
x += 3; printf("after x += 3 : %d\n", x);
x -= 5; printf("after x -= 5 : %d\n", x);
x *= 4; printf("after x *= 4 : %d\n", x);
x /= 3; printf("after x /= 3 : %d\n", x);
x %= 7; printf("after x %%= 7 : %d\n", x);
return 0;
}x starts at 10
after x += 3 : 13
after x -= 5 : 8
after x *= 4 : 32
after x /= 3 : 10
after x %= 7 : 3Three reasons to prefer the compound form, and the third is the one that is not obvious.
- The variable is named once, so a long left-hand side cannot be mistyped on one side only.
- It is shorter to read, once you are used to it.
- The brackets are implied round the right-hand side.
x = a + bisx = x (a + b), notx = x * a + b. This is a guarantee of the language, and it is the opposite of what happens with a careless#define(chapter 22).
#include <stdio.h>
int main(void)
{
int x = 10, a = 2, b = 3;
int y = 10;
x *= a + b; /* x = x * (a + b) */
y = y * a + b; /* the careless reading */
printf("x *= a + b gives %d\n", x);
printf("y = y * a + b gives %d\n", y);
return 0;
}x *= a + b gives 50
y = y * a + b gives 23Assignment Operators and Expressions
The compound operators are not simply text substitution in one other way too: in a[f()] += 1 the expression f() is evaluated once. Written out as a[f()] = a[f()] + 1 it would be evaluated twice.
Assignment converts
The right-hand side is converted to the type of the left-hand side, and chapter 14's third rule applies: something may be lost.
#include <stdio.h>
int main(void)
{
int i;
double d;
char c;
i = 3.99; printf("int i = 3.99 gives %d\n", i);
d = 7 / 2; printf("double d = 7 / 2 gives %.4f\n", d);
c = 'A' + 2; printf("char c = 'A' + 2 gives %c\n", c);
i = -3.99; printf("int i = -3.99 gives %d\n", i);
return 0;
}int i = 3.99 gives 3
double d = 7 / 2 gives 3.0000
char c = 'A' + 2 gives C
int i = -3.99 gives -3double d = 7 / 2; is 3.0. The division is integer division and it happened before the assignment was reached. This is the same trap as chapter 14's and it is worth meeting twice.
The comma operator, briefly
, is also an operator: it evaluates its left operand, discards the result, evaluates its right operand, and yields that. It does impose an order, so it is one of the four operators that sequence their operands.
for (i = 0, j = n - 1; i < j; i++, j--)That is nearly the only place you will see it, and it is the right place: two counters stepped in one for header. Chapter 28.
The commas separating function arguments and separating declarators are not the comma operator. They are punctuation, and they impose no order, which is why printf("%d %d", i++, i) is unsafe.
What this does NOT mean
= is not equality. == is. = stores.
x += 1 is not x++. They have the same effect on x, and x++ as an expression yields the old value while x += 1 yields the new one. As statements they are the same.
Assignment does not return a variable. It yields a value. (a = b) = c does not compile in C, because the result of an assignment is not an lvalue.
Chained assignment does not mean all the variables get the same stored value. Each step converts to its own type. i = d = 3.7 leaves different values in i and d.
x = a + b is not x = x a + b. The right-hand side is bracketed by the language.
An lvalue is not "a variable". An array element, a structure member and *p are all lvalues. A const object is an lvalue that cannot be assigned to.
Assignment Operators and Expressions
Quick revision
=stores the right-hand side, converted to the left-hand type, and yields the stored value.- The left-hand side must be an lvalue: a place, not a value.
=groups right to left, soa = b = c = 0works.- An assignment is an expression:
while ((c = getchar()) != EOF)uses that, andif (x = 5)is the bug that comes from it. - Ten compound operators; the five arithmetic ones are
+= -= *= /= %=. x op= yisx = x op (y): the right-hand side is bracketed, and the left-hand side is evaluated once.- Assignment converts, and may lose data.
double d = 7 / 2;is 3.0. - The comma operator evaluates left then right and yields the right; the commas between function arguments are not it.
Test yourself
1. What is the value of the expression x = 7?
- An assignment yields the value it stored.
2. What does a = b = 5; do, and in what order?
b = 5 is evaluated first, because = groups right to left; it yields 5, which is then assigned to a. Both end as 5.
3. Rewrite total = total + marks[i] * weight; with a compound operator, and say what the brackets do.
total += marks[i] weight;. The right-hand side is implicitly bracketed, so this is total = total + (marks[i] weight), which is what was wanted.
4. x = 10; x *= 2 + 3; What is x?
- The right-hand side is bracketed, so it is
x = x * (2 + 3).
5. Why does while (c = getchar() != EOF) not work?
!= binds tighter than =, so getchar() != EOF is evaluated first and its 1 or 0 is stored in c. The character is lost. Write while ((c = getchar()) != EOF).
6. What is in d after double d = 5 / 2;?
2.0. Both operands are int, so integer division gives 2, which is then converted on assignment.
7. Is (a = b) = c; legal?
No. The result of an assignment in C is a value, not an lvalue, so it cannot appear on the left of another assignment.
What can be asked on this, and how to answer it
"Explain the assignment operators in C." Give plain = with the lvalue requirement and the conversion rule, then the compound operators in a table, then the three properties that earn the extra marks: an assignment is an expression with a value, = groups right to left, and x op= y brackets the right-hand side and evaluates the left-hand side once.
Assignment Operators and Expressions
"What is the difference between x = x + 5 and x += 5?" They have the same effect. += names the variable once, which matters when the left-hand side is long, and it brackets the right-hand side, so x += a + b differs from x = x + a + b only in that the first cannot be misread. Add that x is evaluated once in the compound form.
"Why does if (a = 10) always execute its body?" Because a = 10 is an assignment expression whose value is 10, and any non-zero value is true. It was almost certainly meant to be if (a == 10).
"What is an lvalue?" An expression that designates an object, so it may appear on the left of an assignment: a variable, an array element, a structure member, or *p. 5 and a + b are not lvalues, and a const object is an lvalue that may not be assigned to.
"Explain the comma operator." It evaluates its left operand, discards the value, evaluates its right operand and yields that, with a guaranteed order between them. Its normal use is stepping two counters in a for header. Note that the commas separating function arguments are punctuation, not this operator.
The rest of this subject
These notes are cut from the University's printed syllabus. Open the syllabus itself for the same subject.