Logical operator precedence & associativity

M

marko

/* code start */
int a = 0;
/* expected evaluation and excution order with precedence in mind
/* False(3) , True(1), False(2) */
if ( (a=1) == 0 || 0 != 1 && (a =2) == 1)
putchar('T');
printf("%d", a);
/* code end */

2
"c:\src\test\lcc\test.exe"
Return code 0
Execution time 0.001 seconds
Press any key to continue...

The result shows, code skipped "(a=1) == 0" and expression is false

/* code start */
int a = 0;
/* False(3) , True(1), False(2) */
if ( (a=1) == 0 || 0 != 1 && a == 1)
putchar('T');
printf("%d", a);
/* code end */

T1
"c:\src\test\lcc\test.exe"
Return code 0
Execution time 0.051 seconds
Press any key to continue...

Result shows variable a has been changed as expected though excuted
putchar('T'); statement unexpectedly
logical operator && has higher precedence though both &&, || associates left
to right
so how does evaluation order goes in this code?

feel free to edit comments above in the code to demonstrate execution
order(like number next to True or False)
 
B

Ben Pfaff

marko said:
/* code start */
int a = 0;
/* False(3) , True(1), False(2) */
if ( (a=1) == 0 || 0 != 1 && a == 1)
putchar('T');
printf("%d", a);
/* code end */

T1

(a=1) == 0 is evaluated first. It evaluates to 0, because 1 does
not equal 0. Now "a" has value 1.

Because the left-hand operand of || is 0, now the right-hand
operand must be evaluated. 0 != 1 is true, so the right-hand
operand of && must be evaluated. a == 1 is 1 also, because "a"
was assigned 1, so the result of && is 1. Therefore the result
of || and hence the overall result is 1. Therefore, 'T' is
printed.
 
M

marko

Ben Pfaff said:
marko said:
/* code start */
int a = 0;
/* False(3) , True(1), False(2) */
if ( (a=1) == 0 || 0 != 1 && a == 1)
putchar('T');
printf("%d", a);
/* code end */

T1

(a=1) == 0 is evaluated first. It evaluates to 0, because 1 does
not equal 0. Now "a" has value 1.

Because the left-hand operand of || is 0, now the right-hand
operand must be evaluated. 0 != 1 is true, so the right-hand
operand of && must be evaluated. a == 1 is 1 also, because "a"
was assigned 1, so the result of && is 1. Therefore the result
of || and hence the overall result is 1. Therefore, 'T' is
printed.

--
int main(void){char
p[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.\
\n",*q="kl BIcNBFr.NKEzjwCIxNJC";int i=sizeof p/2;char *strchr();int
putchar(\
);while(*q){i+=strchr(p,*q++)-p;if(i>=(int)sizeof p)i-=sizeof
p-1;putchar(p\
);}return 0;}


thing still puzzles me is precedence. The test line contains both logical
operator && , || without parenthesis and && has higher precedence yet left
and right operands of || was evaluated first. despite number calculation
case, when * operator and + operator are presented without parenthesis,
left and right side of * operator evaluates first with associativity of left
to right.
something seems conflicting to me
please blow out a fog for me if you have right idea

thank you
 
P

pete

thing still puzzles me is precedence.
The test line contains both logical
operator && , || without parenthesis
and && has higher precedence yet left
and right operands of || was evaluated first.

|| has two operands:

(a=1) == 0 is the left operand of ||
0 != 1 && a == 1 is the right operand of ||

The left operand of || was evaluated first.
 
M

marko

thanks

got it!

pete said:
|| has two operands:

(a=1) == 0 is the left operand of ||
0 != 1 && a == 1 is the right operand of ||

The left operand of || was evaluated first.
 
C

Chris Torek

thing still puzzles me is precedence. The test line contains both logical
operator && , || without parenthesis and && has higher precedence yet left
and right operands of || was evaluated first.

"Precedence" is not "order of evaluation".

Strictly speaking, ANSI/ISO C does not even have "operator precedence"
(it uses a fully-factored grammar instead). It does have "order
of evaluation", though -- and one grammar may be equivalent to
another, so a precedence-free factored grammar can have the same
set of valid productions as an operator-precedence grammar.

Operator-precedence grammars are easier for people (i.e., "you" :) )
to deal with, so textbooks on C generally use those.
... when * operator and + operator are presented without parenthesis,
left and right side of * operator evaluates first with associativity
of left to right.

You must memorize this: "precedence is not order of evaluation".

Precedence (which C formally lacks anyway) simply determines which
operator(s) bind to which arguments, in order to build a parse tree
at compile time.

Order of evaluation happens much later, at run time, and -- in C
at least -- is keyed off of things called "sequence points". A
limited set of C operators provide sequence points, specifically
including the logical operators (&& and ||). They dictate that
*once they are evaluated* (whenever that is), first their left side
is evaluated, then a sequence point occurs, then their right side
is evaluated if and only if the result is not yet known.

So, given:

if (f1() || f2() && f3())

the *operator binding* is:

apply && to result of calling f2() and result of calling f3()

and:

apply || to result of calling f1() and result of <bound group>

which can be drawn as the following tree:

||
/ \
f1() &&
/ \
f2() f3()

The tree that *cannot* be drawn (because C does not allow it, either
through a fully-factored grammar that is painful to write out, or
because of an equivalent grammar in your textbook that uses
"operator precedence") is:

&&
/ \
[WRONG] || f3()
/ \
f1() f2()

The runtime *order of evaluation* is required to be:

- call f1, compare its result to zero
- if this produces TRUE (1), take result, do not do the next few:
- call f2, compare result to zero
- if FALSE (0), take result, do not do:
- call f3, compare result to zero

This would be true even if we used parentheses to force the
compile-time binding to match the second tree, by writing:

if ((f1() || f2()) && f3())

However, in this case, the text version of the runtime sequence
might read (pay attention to indentation):

- call f1, compare result to zero
- if this produces TRUE, take result, do not do:
- call f2, compare result to zero
- if result is FALSE, take result, do not do:
- call f3, compare result to zero

In other words, the conditions upon which f3() are called differ
in the second version, even though both versions call f1() first,
then (if at all) f2(), then (if at all) f3().
 
M

marko

"precedence is not order of evaluation".
bet this pit fall will get many people

so compiler scans code left to right always?
and if compiler sees another portion needs to be evaluated prior to move to
next( f1() = false ), it puts off first portion result and evaluate whole
( f2() && f3()) needed portion which may involve another similar process
(and f2() = true, f3() needs to be evaluated in order to evaluate whole f2
&& f3 portion)
kinda recursive.. it's tree i guess( is it "in order" tree or.. ?)

make it terse, compiler evaluates in sequence which depends on that parse
tree?

appreciate for your presentations

Chris Torek said:
thing still puzzles me is precedence. The test line contains both
logical
operator && , || without parenthesis and && has higher precedence yet
left
and right operands of || was evaluated first.

"Precedence" is not "order of evaluation".

Strictly speaking, ANSI/ISO C does not even have "operator precedence"
(it uses a fully-factored grammar instead). It does have "order
of evaluation", though -- and one grammar may be equivalent to
another, so a precedence-free factored grammar can have the same
set of valid productions as an operator-precedence grammar.

Operator-precedence grammars are easier for people (i.e., "you" :) )
to deal with, so textbooks on C generally use those.
... when * operator and + operator are presented without parenthesis,
left and right side of * operator evaluates first with associativity
of left to right.

You must memorize this: "precedence is not order of evaluation".

Precedence (which C formally lacks anyway) simply determines which
operator(s) bind to which arguments, in order to build a parse tree
at compile time.

Order of evaluation happens much later, at run time, and -- in C
at least -- is keyed off of things called "sequence points". A
limited set of C operators provide sequence points, specifically
including the logical operators (&& and ||). They dictate that
*once they are evaluated* (whenever that is), first their left side
is evaluated, then a sequence point occurs, then their right side
is evaluated if and only if the result is not yet known.

So, given:

if (f1() || f2() && f3())

the *operator binding* is:

apply && to result of calling f2() and result of calling f3()

and:

apply || to result of calling f1() and result of <bound group>

which can be drawn as the following tree:

||
/ \
f1() &&
/ \
f2() f3()

The tree that *cannot* be drawn (because C does not allow it, either
through a fully-factored grammar that is painful to write out, or
because of an equivalent grammar in your textbook that uses
"operator precedence") is:

&&
/ \
[WRONG] || f3()
/ \
f1() f2()

The runtime *order of evaluation* is required to be:

- call f1, compare its result to zero
- if this produces TRUE (1), take result, do not do the next few:
- call f2, compare result to zero
- if FALSE (0), take result, do not do:
- call f3, compare result to zero

This would be true even if we used parentheses to force the
compile-time binding to match the second tree, by writing:

if ((f1() || f2()) && f3())

However, in this case, the text version of the runtime sequence
might read (pay attention to indentation):

- call f1, compare result to zero
- if this produces TRUE, take result, do not do:
- call f2, compare result to zero
- if result is FALSE, take result, do not do:
- call f3, compare result to zero

In other words, the conditions upon which f3() are called differ
in the second version, even though both versions call f1() first,
then (if at all) f2(), then (if at all) f3().
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to
spammers.
 
K

Kenny McCormack

"precedence is not order of evaluation".
bet this pit fall will get many people

so compiler scans code left to right always?
and if compiler sees another portion needs to be evaluated prior to move to
next( f1() = false ), it puts off first portion result and evaluate whole
( f2() && f3()) needed portion which may involve another similar process
(and f2() = true, f3() needs to be evaluated in order to evaluate whole f2
&& f3 portion)
kinda recursive.. it's tree i guess( is it "in order" tree or.. ?)

make it terse, compiler evaluates in sequence which depends on that parse
tree?

The point to keep in mind is that the compiler doesn't evaluate
expressions - that happens at runtime.

The other point is that the compiler is free to generate code that will
evaluate the subexpressions in "any" order (obvious caveats apply; IOW,
FSVO "any"), as long as it produces the results that the standard requires.
 
M

marko

Kenny McCormack said:
The point to keep in mind is that the compiler doesn't evaluate
expressions - that happens at runtime.

This is still what i need to know, predicting correct order of evaluation in
a expression. precedence helps to find out the order, but it's not order of
evaluation - thank all for let me know
The other point is that the compiler is free to generate code that will
evaluate the subexpressions in "any" order (obvious caveats apply; IOW,
FSVO "any"), as long as it produces the results that the standard
requires.

is subexpressions mean like...

a = b + c * d

since c * d needs to be performed before addition to b, is c * d a
subexpression? or is it something else?
and compiler can perform finding what c is or what d is in any order.
right?

Is there more that I need to aware like this one? Things that common and
gets people. If it's not too long ( maybe just one or two very well known
pit falls ) I need to know and I'd appreciate for posting.
 
F

Flash Gordon

marko said:
This is still what i need to know, predicting correct order of evaluation in
a expression. precedence helps to find out the order, but it's not order of
evaluation - thank all for let me know

You might think you do but you almost certainly do *not* need to know
the order of evaluation in most situations. What you need to know is
which sub-expressions will be operands of which operators.

The only things in expressions that force a given order of evaluation are:
logical operators && and ||
the left operand is evaluated first and the right *only* if needed

The , operator which is *not* the same as a , in a parameter list

?: where obviously the left operand has to be evaluated first to
determine whether to evaluate the middle or the last operand

function call, where all parameters to a function are evaluated
before the function is called, but they could be evaluated in *any*
order and the order could even change during a run of the program

Of those the only one where I regularly rely on the order of evaluation
is the logical operators, and that is generally for things like
if (ptr != NULL && *ptr==something)
or similar stuff.
is subexpressions mean like...

a = b + c * d

since c * d needs to be performed before addition to b, is c * d a
subexpression? or is it something else?
and compiler can perform finding what c is or what d is in any order.
right?

It could first evaluate b, then d, then c, then do the multiplication
then the addition. Thus even though it has to do the multiplication
before the addition it could still evaluate b first.
Is there more that I need to aware like this one? Things that common and
gets people. If it's not too long ( maybe just one or two very well known
pit falls ) I need to know and I'd appreciate for posting.

Look through the comp.lang.c FAQ at http://c-faq.com/ and you will find
lots of things that people get wrong and probably a lot of things you
believe that are wrong.

Except in specific circumstances the order of evaluation is unspecified
and it could be different each time a piece of code is executed.

Array name are *not* constant pointers
An "array" parameters to a function is actually just a pointer parameter

Never use gets

The list of things people get wrong goes on and on, people without
knowledge are endlessly inventive at finding ways to do things wrong.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,571
Members
45,045
Latest member
DRCM

Latest Threads

Top