coperator-precedenceexpression-evaluation# Operator precedence versus order of evaluation

A friend asked me to explain the difference between operator precedence and order of evaluation in simple terms. This is how I explained it to them :-

Let's take an example -

```
int x;
int a = 2;
int b = 5;
int c = 6;
int d = 4;
x = a * b / (c + d);
```

Here, the final value of `x`

will become `1`

. This is because first, the values of `c`

and `d`

will be added together (`6+4`

), then the values of `a`

and `b`

will be multiplied together (`2*5`

), and finally, the division will take place (`10/10`

), resulting in the final value becoming `1`

, which is then assigned to `x`

.

All of this is specified by operator precedence. In this example, the parentheses force the addition to take place before the multiplication and the division, even though addition has a lower precedence. Also, the multiplication is executed before the division, because multiplication and division have the same precedence, and both of them have the associativity of left-to-right.

Now comes the important part, i.e. the order of evaluation of this expression.

On one system, the order of evaluation may be like this -

```
/* Step 1 */ x = a * b / (c + d);
/* Step 2 */ x = a * 5 / (c + d);
/* Step 3 */ x = a * 5 / (c + 4);
/* Step 4 */ x = a * 5 / (6 + 4);
/* Step 5 */ x = a * 5 / 10;
/* Step 6 */ x = 2 * 5 / 10;
/* Step 7 */ x = 10 / 10;
/* Step 8 */ x = 1;
```

Note that in any step, it is always ensured that the operator precedence is maintained, i.e. even though `b`

was replaced by `5`

in Step 2, the multiplication did not take place until Step 7. So, even though the order of evaluation is different for different systems, the operator precedence is always maintained.

On another system, the order of evaluation may be like this -

```
/* Step 1 */ x = a * b / (c + d);
/* Step 2 */ x = a * b / (6 + d);
/* Step 3 */ x = a * b / (6 + 4);
/* Step 4 */ x = a * b / 10;
/* Step 5 */ x = 2 * b / 10;
/* Step 6 */ x = 2 * 5 / 10;
/* Step 7 */ x = 10 / 10;
/* Step 8 */ x = 1;
```

Again, the operator precedence is maintained.

In the above example, the entire behaviour is well-defined. One reason for this is that all of the variables are different.
In technical terms, the behaviour in this example is well-defined because there are no unsequenced modifications to any variable.
So, on any system, `x`

will always get assigned the value `1`

finally.

Now, let's change the above example to this :-

```
int x;
int y = 1;
x = ++y * y-- / (y + y++);
```

Here, the final value that gets assigned to `x`

varies between systems, making the behaviour undefined.

On one system, the order of evaluation may be like this -

```
/* Step 1 */ x = ++y * y-- / (y + y++); // (y has value 1)
/* Step 2 */ x = ++y * y-- / (1 + y++); // (y still has value 1)
/* Step 3 */ x = ++y * 1 / (1 + y++); // (y now has value 0)
/* Step 4 */ x = 1 * 1 / (1 + y++); // (y now has value 1)
/* Step 5 */ x = 1 * 1 / (1 + 1); // (y now has value 2)
/* Step 6 */ x = 1 * 1 / 2;
/* Step 7 */ x = 1 / 2;
/* Step 8 */ x = 0;
```

Again, the operator precedence is maintained.

On another system, the order of evaluation may be like this -

```
/* Step 1 */ x = ++y * y-- / (y + y++); // (y has value 1)
/* Step 2 */ x = ++y * y-- / (y + 1); // (y now has value 2)
/* Step 3 */ x = ++y * 2 / (y + 1); // (y now has value 1)
/* Step 4 */ x = ++y * 2 / (1 + 1); // (y still has value 1)
/* Step 5 */ x = ++y * 2 / 2; // (y still has value 1)
/* Step 6 */ x = 2 * 2 / 2: // (y now has value 2)
/* Step 7 */ x = 4 / 2;
/* Step 8 */ x = 2;
```

Again, the operator precedence is maintained.

How can I improve this explanation?

Solution

I would prefer an explanation that uses function calls. A function call makes it very obvious that "something needs to be evaluated before applying the operator".

Basic example:

```
int x = a() + b() * c();
```

must be calculated as

```
temp = result_of_b_func_call * result_of_c_func_call
x = result_of_a_func_call + temp
```

due to multiplication having higher **precedence** than addition.

However, the **evaluation order** of the 3 function calls is unspecified, i.e. the functions can be called in any order. Like

```
a(), b(), c()
or
a(), c(), b()
or
b(), a(), c()
or
b(), c(), a()
or
c(), a(), b()
or
c(), b(), a()
```

Another basic example would be to explain operator associativity - like:

```
int x = a() + b() + c();
```

must be calculated as

```
temp = result_of_a_func_call + result_of_b_func_call
x = temp + result_of_c_func_call
```

due to left-to-right associativity of addition. But again the order of the 3 function calls are unknown.

**If function calls is not an option**, I would prefer something like

```
x = a * b + c / d
```

Here it's pretty obvious that there are two sub-expressions, i.e. `a * b`

and `c / d`

. Due to **operator precedence** both of these sub-expressions must be evaluated before the addition but the **order of evaluation** is unspecified, i.e. we can't tell whether the multiplication or the division is done first.

So it can be

```
temp1 = a * b
temp2 = c / d
x = temp1 + temp2
```

**or** it can be

```
temp2 = c / d
temp1 = a * b
x = temp1 + temp2
```

All we know is that the addition must be last.

- Passing the function *cmp(const *void, const *void) as a parameter in C and using it to create a queue?
- Is it safe to use `strstr` to search for multibyte UTF-8 characters in a string?
- Why am I getting a heap-use-after-free error?
- Catch and compute overflow during multiplication of two large integers
- Access a global static variable from another file in C
- "this" pointer in C (not C++)
- Unknown Output of C code
- Yocto- gcc cannot find shared library
- Artifact in one of the triangles in basic OpenGL texture quadrilateral
- How to check if a pointer is valid?
- cJSON Key-Value Parsing
- hi, I am emplementing LZW here and it work great, the problem is that I cant scan and encode a string if it have a (space)
- pthread clean up int/array/struct type variables using pthread_cleanup_push
- Standard way of getting a bit pattern of all ones
- Why doesn't the use of strictly conforming programs and conforming implementations ensure absolute portability?
- Checking emptiness of X macros
- Is it possible to run WebAssembly code async?
- C suddenly messing up values in 2d array
- Socket opening macOS assembly
- GDB outputs arm mnemonics
- What kind of optimization does const offer in C/C++?
- wcstombs segmentation fault
- Fast implementation of complementary error function with 5-digit accuracy
- Ascii to binary calculation is flawed
- what stops GCC __restrict__ qualifier from working
- Why my sig_int() function can't prevent my function from exit in c?
- Allocating user space memory in Linux Kernel
- Determining realloc() behaviour before calling it
- Is it legal to cast a struct to an integer if it has no padding?
- C macro, something weird