Understanding Precedence, Associativity and Order of Evaluation in C/C++

Understanding Precedence, Associativity and Order of Evaluation in C/C++

1-Introduction:

Peace upon you, fellow programmers! Today I'm going to talk about a topic that often confuses beginners and experts alike: the difference between precedence, associativity, and order of evaluation in C/C++. If you've ever wondered why

a + b * c is not the same as (a + b) * c, or why f (g (), h ()) might not call g and h in the order you expect, in this blog post, we will explore the concepts of precedence, associativity, and order of evaluation in C/C++ programming languages, and how they affect the way expressions are evaluated by the compiler.

/***********************************************************************************/

2-Fundamentals:

Let’s starting with defining the meaning of expression:

An expression is composed of one or more operands and yields a result when it is evaluated.

int x = 5;

int y = 7;

int z = x + y; // expression: x + y

In this example, x + y is an expression that adds the values of x and y together. The result of this expression is then assigned to the variable z.

/***********************************************************************************/

3-Grouping Operators and Operands:

Understanding expressions with multiple operators requires understanding the precedence and associativity of the operators and may depend on the order of evaluation of the operands. For example, the result of the following expression depends on how the operands are grouped to the operators:

5 + 10 * 20/2;

The operands to the operator could be 10 and 20, or 10 and 20/2, or 15 and 20, or 15 and 20/2. Understanding such expressions is the topic of the next section. /**********************************************************************************/

4-Precedence and Associativity:

An expression with two or more operators is a compound expression. Evaluating a compound expression involves grouping the operands to the operators.

Precedence and associativity determine how the operands are grouped. That is, they determine which parts of the expression are the operands for each of the operators in the expression.

Programmers can override these rules by parenthesizing compound expressions to force a particular grouping.

In general, the value of an expression depends on how the subexpressions are grouped. Operands of operators with higher precedence group more tightly than operands of operators at lower precedence. Associativity determines how to group operands with the same precedence. For example, multiplication and division have the same precedence as each other, but they have higher precedence than addition. Therefore, operands to multiplication and division group before operands to addition and subtraction. The arithmetic operators are left associative, which means operators at the same precedence group left to right:

? Because of precedence, the expression 3+4*5 is 23, not 35.

? Because of associativity, the expression 20-15-3 is 2, not 8.

As a more complicated example, a left-to-right evaluation of the following expression yields 20:

6 + 3 * 4 / 2 + 2

Other imaginable results include 9, 14, and 36. In C++, the result is 14, because this expression is equivalent to

// parentheses in this expression match default precedence and associativity

((6 + ((3 * 4) / 2)) + 2)

/***********************************************************************************/

5-Order of Evaluation:

Precedence specifies how the operands are grouped. It says nothing about the order in which the operands are evaluated. In most cases, the order is largely unspecified.

In the following expression:

int i = f1() * f2();

we know that f1 and f2 must be called before the multiplication can be done. After all, it is their results that are multiplied. However, we have no way of knowing whether f1 will be called before f2 or vice versa.

For operators that do not specify evaluation order, it is an error for an expression to refer to and change the same object. Expressions that do so have undefined behavior.

As a simple example, the << operator makes no guarantees about when or how its operands are evaluated. As a result, the following output expression is undefined:

int i = 0;

cout << i << " " << ++i << endl; // undefined

Because this program is undefined, we cannot draw any conclusions about how it might behave. The compiler might evaluate ++i before evaluating i, in which case the output will be 1 1. Or the compiler might evaluate i first, in which case the output will be 0 1. Or the compiler might do something else entirely. Because this expression has undefined behavior, the program is in error, regardless of what code the compiler generates.

There are four operators that do guarantee the order in which operands are evaluated. the logical AND (&&) operator guarantees that its left-hand operand is evaluated first. Moreover, we are also guaranteed that the right-hand operand is evaluated only if the left-hand operand is true (short circuiting). The only other operators that guarantee the order in which operands are evaluated are the logical OR (||) operator, the conditional (? :) operator, and the comma (,) operator.

/***********************************************************************************/

6-Order of Evaluation, Precedence, and Associativity:

Order of operand evaluation is independent of precedence and associativity.

In an expression such as f() + g() * h() + j():

? Precedence guarantees that the results of g() and h() are multiplied.

? Associativity guarantees that the result of f() is added to the product of g()

and h() and that the result of that addition is added to the value of j().

? There are no guarantees as to the order in which these functions are called.

If f, g, h, and j are independent functions that do not affect the state of the same objects or perform IO, then the order in which the functions are called is irrelevant. If any of these functions do affect the same object, then the expression is in error and has undefined behavior.

/***********************************************************************************/

7-Summary:

To summarize, precedence determines the grouping of operands in an expression, associativity defines how operands with the same precedence are grouped, and the order of evaluation determines the sequence in which operands are evaluated. Understanding these concepts is crucial for writing correct and efficient code in C/C++. Remember that parentheses can be used to override precedence and associativity, and that the order of evaluation is mostly unspecified except for a few operators.

/***********************************************************************************/

8-Advice & Open question:

#Advice:

When you write compound expressions, two rules of thumb can be helpful:

1. When in doubt, parenthesize expressions to force the grouping that the logic of your program requires.

2. If you change the value of an operand, don’t use that operand elsewhere in the same expression.

An important exception to the second rule occurs when the subexpression that changes the operand is itself the operand of another subexpression. For example, in *++iter, the increment changes the value of iter. The (now changed) value of iter is the operand to the dereference operator. In this (and similar) expressions, order of evaluation isn’t an issue. The increment (i.e., the subexpression that changes the operand) must be evaluated before the dereference can be evaluated. Such usage poses no problems and is quite common.


#Open question:

Order of evaluation for most of the binary operators is left undefined to give the compiler opportunities for optimization. This strategy presents a trade-off between efficient code generation and potential pitfalls in the use of the language by the programmer.

Do you consider that an acceptable trade-off? Why or why not?


Finally, thank you for joining us on this journey to demystify precedence, associativity, and order of evaluation in C/C++. Armed with this knowledge, you'll navigate the intricacies of expressions with confidence and write more robust code. Happy coding!

/***********************************************************************************/

9-References:

1-C++ Primer Fifth Edition, ch4: Expressions.

2-C how to program 7th edition, ch5: Functions, page 193 & 194.

/***********************************************************************************/

10-Appendix:

#Operator Precedence Table:


?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?


要查看或添加评论,请登录

社区洞察

其他会员也浏览了