Remebering the order of precedence

R

Richard

pete said:
That's the same method that I use.

I found any easier way. Always use parenthesis since porting/multi
platform means multi-skills on maintenance and parenthesis takes the
guesswork away. Whenever I met programmers who insisted in minimising
parenthesis because they "knew" precedence there were always problems
down the road with bugs.
 
B

Ben Bacarisse

Richard said:
I found any easier way. Always use parenthesis

A nit: this is not an easier way to do what the OP wants. He may be
reading code that is not fully parenthesised and want some help
remembering.
since porting/multi
platform means multi-skills on maintenance and parenthesis takes the
guesswork away. Whenever I met programmers who insisted in minimising
parenthesis because they "knew" precedence there were always problems
down the road with bugs.

I am not averse to added parentheses but I wonder how for one should
go. CBFalconer suggests bracketing everything but */ and +-, but that
seems unreasonable (and is not what he does in posted code) so how far
does one go?

I'd exclude all unary operators, but then some people find *x++
confusing. Maybe you would bracket expressions that mix pre- and
post-fix operators, but then -x[2] and !feof() seem innocuous enough
to me. Maybe [] and () are to be treated as special in this regard.

It is all very well to say "always use parentheses", but without a
complex set of rules your code will become very cluttered; and with a
complex set of rules it is hardly a simple solution. So, where do you
draw the line? What bracketing rules do you really recommend using in
practise?
 
K

karthikbalaguru

A nit: this is not an easier way to do what the OP wants.  He may be
reading code that is not fully parenthesised and want some help
remembering.

Yes.
It is for reading code that in not fully parenthesised .
Any ideas ?

Thx in advans,
Karthik Balaguru
 
R

Richard Bos

yes, but if you hard code the number of spatial dimensions
your code will not be portable to a space with more than three
dimensions

I'll care about that the moment I start writing code for CERN.

On the subject of precedence, Keith has said everything I intended to.
Just remember the obvious ones, and parenthesize things like & and ?:

Richard
 
R

Richard Bos

karthikbalaguru said:
It is for reading code that in not fully parenthesised .
Any ideas ?

Yes.

When in doubt, refer to the chart. Then complain to the original
programmer.

No, really. Sometimes, life _is_ that simple, and there is no need for
further shortcuts.

Richard
 
T

Tim Rentsch

karthikbalaguru said:
Hi,

Is there a technique to easily remember the order of precedence
of all C operators from high to low priority ?

I think this will save time rather than referring to the chart
for every expression.
Any ideas ?

Two: chunkify; repetition.

To chunkify:
============

There are 15 (too many!) levels of precedence.

The levels fall into groups:

unary, arithmetic, comparison, "boolean", "asymmetric";

Each group is a contiguous band of (two or more) levels
of precedence.

The unary group prefers postfix to prefix: *p++ is *(p++).
It has only two levels.

The arithmetic group has levels for multiplying, adding,
and shifting. The level for shifting is below adding, even
though more sensibly these would be lumped in with multiplying.
Of course multiplying is higher precedence than adding.

The comparison group as all the usual relational operators,
except that == and != have lower precedence than all the
others. I don't know why. This group has two levels.

The "boolean" group has five levels, and only one operator
in each level. The short circuiting ones are at bottom,
the non-short circuiting ones at the top. "And" operators
have higher precedence than the corresponding "or" operators,
so the only "boolean" operator left is bitwise exclusive-or,
which is in between & and |.
The reason the bitwise "boolean" operators have lower
precedence than the relational operators is that origially
C didn't have the short-circuiting (&&,||) versions, so
it made sense to use & and | for boolean combinations.

The "asymmetric" group has three levels:

"if/else" (?:), assignment, "sequence" (,)

Each level has only one operator per level, except of course
there are a zillion subvariations on assignment.

It make sense for assignment to be in the middle of the three
levels, so that: (a) an "if/else" expression, which just has
a value, can be assigned to something without needing parentheses;
and, (b) a single expression can contain several independent
assignments, without needing any parentheses.

Knowing the groups, it's easy to put specific operators in
the right level within each group.

Adding up the groups:

unary: 2 levels
arithmetic: 3 levels
comparison: 2 levels
"boolean": 5 levels
"asymmetric": 3 levels
-----------------------
total 15 levels


For repetition:
===============

Every time you look at an expression and can't remember
which way the precedence works, do this:

1. Write out, by hand and to the best of your memory,
a full precedence table;
2. Look up a full precedence table (several are
available online);
3. Still looking at the precedence table you looked
up, again write out a full precedence table.

Remember in step 1 to include casting, sizeof, and
the prefix unary operators that are hardly ever used.

If you discipline yourself to write parentheses only
when they are necessary because of precedence relationships,
in a very short time you will know the whole table cold.
 
T

Tim Rentsch

Keith Thompson said:
When in doubt, use parentheses.

This advice is commonly given, but actually makes the problem
worse rather than better. Adopting the discipline never to use
redundant parentheses forces the learning to take place.

If one wants to indicate precedence for the benefit of later
readers, another way of doing that is with white space:

a+b << 2
a | b<<2

This technique indicates both what precedence relationships are
in effect and that the author realizes that such expressions
don't need parentheses to cause the groupings shown. Because
it provides information redundantly, checking the redundant
information can increase confidence both in the code and in the
author.
 
T

Tim Rentsch

CBFalconer said:
Just remember that multiplication (and division) takes precedence
over addition (and subtraction). For anything more complicated use
parentheses. You won't wonder, and your readers won't wonder.

Common advice, but advice that perpetuates the problem
he's trying to solve.
 
T

Tim Rentsch

James Kuyper said:
I've never found any method for memorizing such stuff that works better
than the following: keep referring to the references whenever you are
confused, until it no long confuses you.

That probably doesn't count as "easy" - but it works pretty well for me.

Trying to write it down before you look it up, and
writing it down again a little while later after
looking it up, takes a little more effort up front but
usually produces earlier, and more reliable, results.

I learned this trick from a friend of mine who teaches C
(among other things) at a community college level. It works
great!
 
T

Tim Rentsch

Richard said:
I found any easier way. Always use parenthesis since porting/multi
platform means multi-skills on maintenance and parenthesis takes the
guesswork away. Whenever I met programmers who insisted in minimising
parenthesis because they "knew" precedence there were always problems
down the road with bugs.

Try this: when confronted with such people, tell them it's okay
to omit redundant parentheses, but they have to indicate the
precedence they think is happening using another method (such as
with white space, as shown in a recent posting). During code
reviews point out any errors. If precedence problems come up
during the code reviews, they will be educated and the code will
get better. If precedence problems don't come up during the code
reviews, then it wasn't their understanding of precedence that
was causing the problems. Either way, there will be better
understanding of what problems cause(d) bugs, fewer bugs or at
least easier to track down bugs, and more competent developers
all around.
 
P

Phil Carmody

Tim Rentsch said:
This advice is commonly given, but actually makes the problem
worse rather than better. Adopting the discipline never to use
redundant parentheses forces the learning to take place.

If one wants to indicate precedence for the benefit of later
readers, another way of doing that is with white space:

a+b << 2
a | b<<2

This technique indicates both what precedence relationships are
in effect and that the author realizes that such expressions
don't need parentheses to cause the groupings shown. Because
it provides information redundantly, checking the redundant
information can increase confidence both in the code and in the
author.

But what if you see the expressions:

a + b<<2
a|b << 2

The technique of merely adding whitespace has *obfuscated* the
precedence relationship. A tool in the wrong hands...

If you see such errors, then you have to undo them. If you see
redundant brackets, you don't have to undo them, so in some ways
they are better.

I do like the technique you suggest, I use it myself, but that's
only because I trust my knowledge of precedence. I don't trust
90% of other programmers I've worked with to get it right. (And
I demonstrate this trust in myself by *always* having the precedence
chart pinned on the wall next to my desk. Over the years I notice
that I refer to it less and less.)

Phil
 
T

Tim Woodall

Common advice, but advice that perpetuates the problem
he's trying to solve.
Amen to that. Unfortunately, at work our code has to be gcc/g++ warning
clean (-Wall) and gcc demands gratuitous parenthesis when run with
-Wall.

tim@feynman:~$ gcc -Wall -o main main.c
main.c: In function 'main':
main.c:7: warning: suggest parentheses around && within ||

I'm always surprised that it insists on parenthesis for a&&b||c but
doesn't for *ptr++;. It does for a+b<<c but not for a+b*c.

:-(

Tim.
 
T

Tim Rentsch

Tim Woodall said:
Amen to that. Unfortunately, at work our code has to be gcc/g++ warning
clean (-Wall) and gcc demands gratuitous parenthesis when run with
-Wall.

tim@feynman:~$ gcc -Wall -o main main.c
main.c: In function 'main':
main.c:7: warning: suggest parentheses around && within ||

I'm always surprised that it insists on parenthesis for a&&b||c but
doesn't for *ptr++;. It does for a+b<<c but not for a+b*c.

:-(

You might want to try lobbying for -Wno-parentheses in addition
to -Wall. If you do, be prepared with counter-arguments (and
also alternative techniques) for the people who haven't yet
realized what the consequences are of -Wparentheses.
 
T

Tim Rentsch

Phil Carmody said:
But what if you see the expressions:

a + b<<2
a|b << 2

The technique of merely adding whitespace has *obfuscated* the
precedence relationship. A tool in the wrong hands...

If you see such errors, then you have to undo them. If you see
redundant brackets, you don't have to undo them, so in some ways
they are better.

They can be, depending on the local community. I like to
think that organizations are smart enough either to have
code reviews that catch such things (in which case pretty
soon they will happen much less often), or to automate
the checking phase for such things, since these violations
could be checked for purely mechanically. Alas, I know
that isn't always the case; kind of like a multi-player
"Prisoner's Dilemma."

I do like the technique you suggest, I use it myself, but that's
only because I trust my knowledge of precedence. I don't trust
90% of other programmers I've worked with to get it right. (And
I demonstrate this trust in myself by *always* having the precedence
chart pinned on the wall next to my desk. Over the years I notice
that I refer to it less and less.)

At first it needs to be reinforced with repetition and rote learning.
Also in a group setting it needs to be checked, because some people
won't check themselves unless they know that others will check them.
As time goes by, it will become more and more reflexive, until a
large enough set of group members always just know the whole table;
at that point the situation will flip and everyone will make the
transition. Community culture can be a powerful development tool.
 
K

Keith Thompson

[...]

Let me revise my advice:

When in doubt, use parentheses.
Try to be in doubt as rarely as possible.

I wouldn't go so far as to avoid *all* redundant parentheses. C has
so many precedence rules that, even if you've memorized them all, it's
likely that anyone reading your code hasn't.
 
R

Richard Tobin

Keith Thompson said:
On the other hand, some precedence relationships should be easy to
remember with a bit of experience. I have no trouble remembering
the following, listed from tight to loose binding:

Unary (- + sizeof ~ !)
Multiplicative (* / %)
Additive (+ -)
Comparison (== != < <= > >=)
Logical (&& ||)
Assignment (= += *= et al)
Comma (,)

Most of these should be obvious to most programmers, even without
any C-specific experience, just from experience of arithmetic
and understanding how they are commonly used.

In everyday arithmetic, unary minus binds very tightly, and the other
unary operators follow suit. In arithmetic, division and
multiplication bind more tightly than addition and subtraction.
(If C had an exponentiation operator, it would bind more tightly
than multiplication.)

It would be very strange if assignment or comparison bound more
tightly than arithmetic, since you frequently want to copmare
expressions, and similarly it's natural that the logical operators
bind more loosely than comparison.

If you have any familiarity with logic, the analogy between
conjunction/disjunction and multiplication/addition makes the
relative precedence of && and || natural.

If you know what the comma operator is for, it's natural for it
to bindly very loosely.

The only really non-obvious case in the list above is the position of
assignment relative to the comparison and logical operators. (Not
surprisingly, this is a common error, which good compilers try to warn
against.) And it's not obvious whether the bitwise operators should be
analogous to the (shortcut) logical operators or to the arithmetic
ones.

-- Richard
 
T

Tim Rentsch

Most of these should be obvious to most programmers, even without
any C-specific experience, just from experience of arithmetic
and understanding how they are commonly used.

In everyday arithmetic, unary minus binds very tightly, and the other
unary operators follow suit. In arithmetic, division and
multiplication bind more tightly than addition and subtraction.
(If C had an exponentiation operator, it would bind more tightly
than multiplication.)

It would be very strange if assignment or comparison bound more
tightly than arithmetic, since you frequently want to copmare
expressions, and similarly it's natural that the logical operators
bind more loosely than comparison.

If you have any familiarity with logic, the analogy between
conjunction/disjunction and multiplication/addition makes the
relative precedence of && and || natural.

If you know what the comma operator is for, it's natural for it
to bindly very loosely.

The only really non-obvious case in the list above is the position of
assignment relative to the comparison and logical operators. (Not
surprisingly, this is a common error, which good compilers try to warn
against.) And it's not obvious whether the bitwise operators should be
analogous to the (shortcut) logical operators or to the arithmetic
ones.

Mostly I agree with the above, but there are two or three notable
exceptions.

The least notable exception is the shift operators, which seem
natural to put in with the multiply-level operators.

At the other end of the precedence scale, to me assignment makes
sense where it is. All the operators above it (not counting ++
and --) yield values without side-effects, and you want to do
something with the value, so assignment should be right near the
bottom so those values can be stored. The one operator below
assignment, comma, clearly seems designed to partition operators
that have side-effects, so it naturally goes below assignment.

The precedence choice that seems most wrong is the bitwise boolean
operators; these would be better just above the comparison
operators (both relational and equality), rather than just below.
I remember reading somewhere that Dennis Ritchie said he would
change that now if he could (originally C didn't have && and ||)
but of course it's too late now to change it.
 
T

Tim Rentsch

Keith Thompson said:
[...]

Let me revise my advice:

When in doubt, use parentheses.
Try to be in doubt as rarely as possible.

I wouldn't go so far as to avoid *all* redundant parentheses. C has
so many precedence rules that, even if you've memorized them all, it's
likely that anyone reading your code hasn't.

Although, if everyone followed your revised advice,
it's only a matter of time before they all would.
 
K

Keith Thompson

Tim Rentsch said:
Keith Thompson said:
Phil Carmody said:
When in doubt, use parentheses.

This advice is commonly given, but actually makes the problem
worse rather than better. Adopting the discipline never to use
redundant parentheses forces the learning to take place.
[...]

Let me revise my advice:

When in doubt, use parentheses.
Try to be in doubt as rarely as possible.

I wouldn't go so far as to avoid *all* redundant parentheses. C has
so many precedence rules that, even if you've memorized them all, it's
likely that anyone reading your code hasn't.

Although, if everyone followed your revised advice,
it's only a matter of time before they all would.

Hey, I never claimed to be consistent.

Except, you know, when I did.

Ok, how about this:

When in doubt, use parentheses.
Try to be in doubt as rarely as possible.
Keep in mind that your readers may still be in doubt, and have mercy
on those who are not yet as enlightened as you are.
 
T

Tim Rentsch

Keith Thompson said:
Tim Rentsch said:
Keith Thompson said:
[...]
When in doubt, use parentheses.

This advice is commonly given, but actually makes the problem
worse rather than better. Adopting the discipline never to use
redundant parentheses forces the learning to take place.
[...]

Let me revise my advice:

When in doubt, use parentheses.
Try to be in doubt as rarely as possible.

I wouldn't go so far as to avoid *all* redundant parentheses. C has
so many precedence rules that, even if you've memorized them all, it's
likely that anyone reading your code hasn't.

Although, if everyone followed your revised advice,
it's only a matter of time before they all would.

Hey, I never claimed to be consistent.

Except, you know, when I did.

Exactly. Why do people have such trouble understanding
that? ;)
Ok, how about this:

When in doubt, use parentheses.
Try to be in doubt as rarely as possible.
Keep in mind that your readers may still be in doubt, and have mercy
on those who are not yet as enlightened as you are.

Oh, the previous version was definitely better. Can we
improve on that?

When in doubt, use parentheses.
Try to be in doubt as rarely as possible.

If writing part of a program that you think others might
find confusing, try to write it in a way that removes both
confusion and doubt -- perhaps using redundant parentheses,
but if possible without. There's more than one way to skin
a cat.
 

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,772
Messages
2,569,588
Members
45,100
Latest member
MelodeeFaj
Top