Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
What is the difference between operator precedence and order of evaluation?
When doing something simple such as this
int a=1;
int b=2;
int c=3;
printf("%d\n", a + b * c);
then I was told that operator precedence guarantees that the code is equivalent to
a + (b * c)
, since *
has higher precedence than +
. And so the result is guaranteed to be 7 and not 9.
However, when I modify the above example like this:
#include <stdio.h>
int a (void) { printf("%s ",__func__); return 1; }
int b (void) { printf("%s ",__func__); return 2; }
int c (void) { printf("%s ",__func__); return 3; }
int main (void)
{
printf("%d\n", a() + b() * c());
return 0;
}
Then I get the output a b c 7
. How is this possible?
Shouldn't operator precedence guarantee that b()
is executed before a()
?
2 answers
Shouldn't operator precedence guarantee that b() is executed before a()?
No. Operator precedence says what to do with values in an expression once you have its input values. Order of evaluation is about how to get the values. The two can be different without any inconsistency.
In your second example, the functions in the expression are evaluated in left to right order because of the compiler's order of evaluation. Those three results are then used to compute the expression value with multiplication first because of the compiler's order of precedence.
0 comment threads
When parsing a program, a compiler creates a tree of nodes. The operator precedence plays a role in how those nodes get organized. Once the tree is complete and considered valid, it walks the tree and outputs the corresponding instructions (in case of a C/C++ compiler, the instructions are assembly code; for other types of languages, such as Java, it may be "byte code"; for JavaScript it used to be kept in a tree of nodes, now we have basic assembly code in most cases).
So for an operation such as a + b * c
, you get:
+
/ \
/ *
a / \
b c
Then we walk the tree from left to right. That means we find a +
and go toward the left side where we find a
. We get that value (which in your case means calling the a()
function).
Then we walk back up to the +
and walk down toward the *
. The *
has two branches, we first follow the one of the left and find the b
.
Once b
was saved, we come back to the *
and go down the other side and find c
. Again, we evaluate c
.
Now that we have all the branch of the *
, it means we have all the values and we can therefore perform the *
evaluation and save that temporary result (i.e. temp = b * c
). Save that result and go up again.
Finally, we are back on the +
node, we have a
and temp
and we can compute the addition and return that as the result of the expression.
In assembly language, we most often use the stack to save the temporary values. With Intel processor and an ABI that returns the result in register RAX
, we would get something more or less like this:
CALL a
PUSH RAX
CALL b
PUSH RAX
CALL c
PUSH RAX ; assuming no optimizations
POP RAX
POP RCX
IMUL RAX, RCX
PUSH RAX ; again, assuming no optimizations
POP RAX
POP RCX
ADD RAX, RCX
RET
From all the compilers I know of, none of them would reorder the CALL
because that's part of the definition of the language.
There is one weird one, the cl compiler on MS-Windows will compute the parameters to a function call from right to left. This can be a gotcha if you want to make that same code work on other systems (Linux... are there others these days?!) since the convention is to do it left to right.
call(p1(), p2(), p3(), p4());
// Microsoft compiler:
_p4 = p4();
_p3 = p3();
_p2 = p2();
_p1 = p1();
call(p1, p2, p3, p4);
// Linux compiler:
_p1 = p1();
_p2 = p2();
_p3 = p3();
_p4 = p4();
call(p1, p2, p3, p4);
I'd bet there are other exceptions. This is the one I know of.
1 comment thread