Evaluation of Expressions in C

Having seen how various types of arithmetic and assignment operators work, it is now time to analyze how complex expressions containing such operators are evaluated by the C language.

In this lesson, we will see that operators have precedence, and that C evaluates expressions according to a specific order. Furthermore, it is possible to force C to evaluate an expression in a different order by using parentheses, just like in mathematical expressions.

Order of Evaluation of Expressions

The C language, like many other programming languages, evaluates expressions according to a specific order. This order is determined by the precedence of operators. In other words, in complex expressions, C evaluates operators with higher precedence first, and then those with lower precedence.

So far, we have seen arithmetic operators, assignment operators, and increment and decrement operators. Below is a partial table showing the precedence order of these operators and their associativity:

Operator Syntax Precedence Associativity
Increment (postfix) ++ 5 Left
Decrement (postfix) -- 5 Left
Increment (prefix) ++ 4 Right
Decrement (prefix) -- 4 Right
Plus (unary) + 4 Right
Minus (unary) - 4 Right
Multiplication * 3 Left
Division / 3 Left
Modulo % 3 Left
Addition + 2 Left
Subtraction - 2 Left
Assignment = 1 Right
Compound assignments +=, -=, *=, /=, %= 1 Right
Table 1: Partial precedence order of arithmetic and assignment operators in the C language

As seen in the table, the precedence value of operators ranges from 1 (lowest) to 5 (highest). This means that, when evaluating an expression, C first evaluates the operators with higher precedence, then those with lower precedence. Additionally, if two operators have the same precedence, C evaluates those on the left first, then those on the right.

The above table is partial; other operators will be covered later.

To better understand, let's look at an example. Consider the following expression:

x = y += ++z - w++ * -2;

The expression includes assignment, arithmetic, and increment operators. First, C evaluates the postfix increment operator on w, so the expression can be rewritten as:

x = y += ++z - (w++) * -2;

Next, the compiler evaluates the prefix increment and unary minus operators, so the expression becomes:

x = y += (++z) - (w++) * (-2);

Then, the compiler evaluates multiplication and subtraction:

x = y += (++z) - ((w++) * (-2));

Next, subtraction is evaluated:

x = y += ((++z) - ((w++) * (-2)));

Finally, the assignment operators are evaluated:

x = (y += ((++z) - ((w++) * (-2))));

Now that we have broken down the expression, let’s assign initial values to the variables y, z, and w, and evaluate the expression:

int z = 1, w = 2, y = 5;

Following the order:

  1. w++ evaluates w as 2, then increments w to 3; however, the expression uses the value of w before the increment:

    x = (y += ((++z) - (2 * (-2))));
    w = 3;
    
  2. ++z increments z to 2 and then uses that value:

    x = (y += ((2) - (2 * (-2))));
    
  3. -2 is a unary operator, so it’s evaluated before multiplication:

    x = (y += ((2) - (2 * (-2))));
    
  4. 2 * (-2) involves a multiplication operator, evaluated before subtraction:

    x = (y += ((2) - (-4)));
    
  5. 2 - (-4) is then evaluated:

    x = (y += 6);
    
  6. Next, the compound assignment operator is evaluated:

    y = 11;
    x = y;
    
  7. Finally, x = y is evaluated.

At the end of the expression evaluation, the variables have the following values:

z = 2;
w = 3;
y = 11;
x = 11;

In any case, although the C language defines an operator precedence order, it is always preferable to use parentheses to make code more readable.

Hint

When in Doubt, Use Parentheses

When writing a complex expression but you are unsure of the operator precedence, it’s good practice to use parentheses to make the code more readable.

Hint

Avoid Mixing Assignments and Arithmetic in the Same Expression

Another good programming practice in C is to avoid combining multiple assignments and arithmetic operations in the same expression. If the expression is complex, it might be difficult to understand which operation is executed first and which comes later.

For example, it is better to transform the following expression:

x = y += ++z - w++ * -2;

into:

++z;
y += z - w * -2;
x = y;
w++;

Precedence and Sub-Expressions

In the previous section, we saw that operators have precedence and associativity in the C language. However, parentheses can always be used to modify this order, exactly as in mathematical expressions.

For example, if we want to perform an addition before a multiplication, we can write:

int x = (2 + 3) * 4;

In this case, the result will be 20 because the addition is executed before the multiplication.

Definition

Parentheses and Sub-Expressions

In the C language, just like in mathematical expressions, parentheses can be used to change the order of operator evaluation.

A sub-expression is an expression enclosed in parentheses that has higher precedence than any external operator.

However, parentheses and sub-expressions carry a caveat. In C, the evaluation order of two sub-expressions with the same precedence is not defined.

Let’s look at an example:

x = (a + b) * (c + d);

In this expression, the two sub-expressions a + b and c + d have the same precedence. However, the order in which these sub-expressions are evaluated is not defined. This means the compiler can evaluate them in any order. It may evaluate c + d first and then a + b, or vice versa.

As long as these are purely mathematical expressions without side effects, this is not a problem. In such cases, the evaluation order does not affect the result. However, when combining sub-expressions and assignments, the result becomes undefined.

For example, the result of this expression is undefined:

x = (a += 1) * (a = b + 1);

In this case, the compiler might evaluate a += 1 and then a = b + 1, or the reverse. In either case, the result would be different.

Note

Avoid Combining Sub-Expressions and Assignments

When writing a program in C, it is good practice to avoid combining sub-expressions and assignments. The reason is that the evaluation order of sub-expressions with the same precedence is not defined.

In Summary

In this lesson, we saw that operators have precedence and associativity in the C language. However, parentheses can always be used to modify this order, just as in mathematical expressions.

The use of parentheses leads to the creation of sub-expressions. These are expressions enclosed in parentheses and have higher precedence than any surrounding operator.

However, parentheses and sub-expressions come with a caveat. In C, the evaluation order of two sub-expressions with the same precedence is not defined.