5.1. How Expressions Are EvaluatedBefore we consider specific operators in detail, this section explains a few fundamental principles that will help you understand how C expressions are evaluated. The precedence and associativity of operators are obviously important in parsing compound expressions, but sequence points and lvalues are no less essential to understanding how a C program works. 5.1.1. LvaluesAn lvalue is an expression that designates an object. The simplest example is the name of a variable. The initial "L" in the term originally meant "left": because an lvalue designates an object, it can appear on the left side of an assignment operator, as in leftexpression = rightexpression.[*] Other expressionsthose that represent a value without designating an objectare called, by analogy, rvalues. An rvalue is an expression that can appear on the right side of an assignment operator, but not the left. Examples include constants and arithmetic expressions.
An lvalue can always be resolved to the corresponding object's address, unless the object is a bit-field or a variable declared with the register storage class (see the section "Storage Class Specifiers" in Chapter 11). The operators that yield an lvalue include the subscript operator [ ] and the indirection operator *, as the examples in Table 5-2 illustrate (assume that array has been declared as an array and ptr as a pointer variable).
An object may be declared as constant. If this is the case, you can't use it on the left side of an expression, even though it is an lvalue, as the following example illustrates: int a = 1; const int b = 2, *ptr = &a; b = 20; // Error: b is declared as const int. *ptr = 10; // Error: ptr is declared as a pointer to const int. In this example, the expressions a, b, ptr, and *ptr are all lvalues . However, b and *ptr are constant lvalues. Because ptr is declared as a pointer to const int, you cannot use it to modify the object it points to. For a full discussion of declarations, see Chapter 11. The left operand of an assignment, as well as any operand of the increment and decrement operators, ++ and --, must be not only an lvalue, but also a modifiable lvalue. A modifiable lvalue is an lvalue that is not declared as a const-qualified type (see "Type Qualifiers" in Chapter 11), and that does not have an array type. If a modifiable lvalue designates an object with a structure or union type, none of its elements must be declared, directly or indirectly, as having a const-qualified type. 5.1.2. Side Effects and Sequence PointsIn addition to yielding a value, the evaluation of an expression can result in other changes in the execution environment, called side effects. Examples of such changes include modifications of a variable's value, or of input or output streams. During the execution of a program, there are determinate points at which all the side effects of a given expression have been completed, and no effects of the next expression have yet occurred. Such points in the program are called sequence points . Between two consecutive sequence points, partial expressions may be evaluated in any order. As a programmer, you must therefore remember not to modify any object more than once between two consecutive sequence points. An example: int i = 1; // OK. i = i++; // Wrong: two modifications of i; behavior is undefined. Because the assignment and increment operations in the last statement may take place in either order, the resulting value of i is undefined. Similarly, in the expression f( )+g( ), where f( ) and g( ) are two functions, C does not specify which function call is performed first. It is up to you the programmer to make sure that the results of such an expression are not dependent on the order of evaluation. Another example: int i = 0, array[ ] = { 0, 10, 20 }; // ... array[i] = array[++i]; // Wrong: behavior undefined. array[i] = array[i + 1]; ++i; // OK: modifications separated by a sequence // point. The most important sequence points occur at the following positions:
Thus the expression ++i < 100 ? f(i++) : (i = 0) is permissible, as there is a sequence point between the first modification of i and whichever of the other two modifications is performed. 5.1.3. Operator Precedence and AssociativityAn expression may contain several operators. In this case, the precedence of the operators determines which part of the expression is treated as the operand of each operator. For example, in keeping with the customary rules of arithmetic, the operators *, /, and % have higher precedence in an expression than the operators + and -. For example, the following expression: a - b * c is equivalent to a - (b * c). If you intend the operands to be grouped differently, you must use parentheses, thus: (a - b) * c If two operators in an expression have the same precedence, then their associativity determines whether they are grouped with operands in order from left to right, or from right to left. For example, arithmetic operators are associated with operands from left to right, and assignment operators from right to left, as shown in Table 5-3. Table 5-4 lists the precedence and associativity of all the C operators.
The last of the highest-precedence operators in Table 5-4, (type name){list}, is the newest, added in C99. It is described in "Compound literals," later in this chapter. A few of the operator tokens appear twice in the table. To start with, the increment and decrement operators , ++ and --, have a higher precedence when used as postfix operators (as in the expression x++) than the same tokens when used as prefix operators (as in ++x). Furthermore, the tokens +, -, *, and & represent both unary operators that is, operators that work on a single operandand binary operators , or operators that connect two operands. For example, * with one operand is the indirection operator, and with two operands, it is the multiplication sign. In each of these cases, the unary operator has higher precedence than the binary operator. For example, the expression *ptr1 * *ptr2 is equivalent to (*ptr1) * (*ptr2). |