Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

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?

+14
−1

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()?

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.
Why should this post be closed?

1 comment thread

General comments (10 comments)

2 answers

You are accessing this answer with a direct link, so it's being shown above all other answers regardless of its score. You can return to the normal view.

+0
−0
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.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

0 comment threads

+0
−0

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.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

0 comment threads

Sign up to answer this question »