Thread: Operator Precedence?

  1. #16
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    The fog is starting to clear, but only slightly.
    I can see why post-incrementing could cause problems, but pre-increments are supposed to happen before the values are used...
    As for functions like a() / b(), they aren't listed in the Operator Precedence, so they're definitely undefined.
    Quote Originally Posted by brewbuck View Post
    Precedence has nothing to do with order of evaluation. It has to do with how the operators bind to operands.
    Then why does that site (& MSDN...) say "The operators at the top of this list are evaluated first."?

    By "how the operators bind to operands", are you talking about the Left to Right or Right to Left Associativity?

  2. #17
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by cpjust View Post

    Then why does that site (& MSDN...) say "The operators at the top of this list are evaluated first."?
    Because they are. But what operator is involved in evaluating "x"? None at all.

  3. #18
    Just Lurking Dave_Sinkula's Avatar
    Join Date
    Oct 2002
    Posts
    5,005
    Quote Originally Posted by cpjust View Post
    The fog is starting to clear, but only slightly.
    I can see why post-incrementing could cause problems, but pre-increments are supposed to happen before the values are used...
    As for functions like a() / b(), they aren't listed in the Operator Precedence, so they're definitely undefined.


    Then why does that site (& MSDN...) say "The operators at the top of this list are evaluated first."?

    By "how the operators bind to operands", are you talking about the Left to Right or Right to Left Associativity?
    I think what you may be looking for are sequence points.
    7. It is easier to write an incorrect program than understand a correct one.
    40. There are two ways to write error-free programs; only the third one works.*

  4. #19
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Quote Originally Posted by cpjust View Post
    I can see why post-incrementing could cause problems, but pre-increments are supposed to happen before the values are used...
    Used which time? One occurrence of x could be evaluated before it sees the one with the ++ in front of it.
    The compiler doesn't have to make a pass over the entire expression beforehand to perform all preincrements and another pass afterwards to perform all the post increments. It is free to do each of these when it first encounters the per/post increments whilst it evaluates the expression, which is likely to be more efficient, and both ways are allowed by the standard.

    I'm not sure why you can see there would be a problem with postincrement, and not see essentially the same problem with preincrement.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  5. #20
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    Quote Originally Posted by Dave_Sinkula View Post
    I think what you may be looking for are sequence points.
    Aha! OK, now after my 2nd glass of wine, things are starting to make sense.

    When I saw this statement:
    Code:
    a[i] = i++;
    (assuming i = 0 before that line)
    my first instinct would have been that it means something like this:
    Code:
    a[i] = i;  // i.e.  a[0] = 0;
    i = i + 1;
    but (if it wasn't undefined), since ++ is higher than = it would probably end up being something like:
    Code:
    temp = i;
    i = i + 1;
    a[i] = temp;  // i.e.  a[1] = 0;
    So the reason why statements like that are undefined are:
    1. To make compilers & optimizers easier to write.
    and
    2. To prevent us from making some stupid mistakes.

    Is that about right, or did I miss anything?

  6. #21
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    Quote Originally Posted by iMalc View Post
    I'm not sure why you can see there would be a problem with postincrement, and not see essentially the same problem with preincrement.
    Well I was thinking about the old value being returned and used in the other lower precedence operation, then incremented, so who knows when the other occurances of that variable should change to the incremented value... But now it's starting to make sense.

  7. #22
    Just Lurking Dave_Sinkula's Avatar
    Join Date
    Oct 2002
    Posts
    5,005
    Quote Originally Posted by cpjust View Post
    Aha! OK, now after my 2nd glass of wine, things are starting to make sense.

    When I saw this statement:
    Code:
    a[i] = i++;
    (assuming i = 0 before that line)
    my first instinct would have been that it means something like this:
    Code:
    a[i] = i;  // i.e.  a[0] = 0;
    i = i + 1;
    but (if it wasn't undefined), since ++ is higher than = it would probably end up being something like:
    Code:
    temp = i;
    i = i + 1;
    a[i] = temp;  // i.e.  a[1] = 0;
    So the reason why statements like that are undefined are:
    1. To make compilers & optimizers easier to write.
    and
    2. To prevent us from making some stupid mistakes.

    Is that about right, or did I miss anything?
    I believe so. But then again I've read and re-read that FAQ dozens and dozens of time -- but the beer is just not helping.

    What I try to take away is remembering where to revisit when I think certain constructs look suspect and to in general avoid such critters like the plague.

    [aside]Perhaps my true epiphany was one day seeing that 'more C = less assembly, in some cases', and even being a minimalist this struck a chord.[/aside]
    7. It is easier to write an incorrect program than understand a correct one.
    40. There are two ways to write error-free programs; only the third one works.*

  8. #23
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    This may or may not help, but I think I'll throw it in just for the sake of it
    Give a formula such as: x = y / z
    In math, it would be easy: just divide y with z.

    Now, let's try to try play a compiler
    Because the data needed for the instructions needs to be stored inside the processor registers, the compiler needs to read the data, and then store it in a register before performing the opcode.

    So, basically it should happen like:
    Store the value of variable y into register A.
    Store the value of variable z into register B.
    Perform opcode.

    But there's no telling in what order it might store the data, so it might just as well do:
    Store the value of variable z into register B.
    Store the value of variable y into register A.

    So, if we re-write the formula as z = x / ++x
    The compiler can do

    (Left to right)
    Store the value of x (left side) into register A.
    Store the value of x (right side) into register B and increment it.
    Perform opcode.

    But on the other hand, it might do:

    (Right to left)
    Store the value of variable x (right side) in register B and increment it.
    (At this point, the value of x is now original x + 1, which must be reflected inside the variable x. In debug mode, I assume the compiler will write back the new value to the variable x, but in release, I assume it might just try to move the contents of register B directly into register A.)
    Store the value of variable x (left side) [which is now original x + 1] into register A.
    Perform opcode.

    Which would invoke undefined behaviour. Certainly, the compiler can do other optimizations, too. If the values are of different variables, then technically it shouldn't matter since the value of those variables would not affect each other during the phase the compiler fetches them and puts them into registers. So no bad behaviour there.

    Anyway, I think that's a little of how I see it.
    (Although I don't know if it's 100% what happens, especially with the ++x.)
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  9. #24
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    Given
    f() + g() * h();
    The only thing the operator precedence table tells you is that * happens before +, and that's all.
    In which order the 3 functions are called is totally up to the compiler (evaluating the operands).

    On a register-rich machine, you could imagine f() being called first, but on a register-poor machine, perhaps it's called last.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  10. #25
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    Quote Originally Posted by Salem View Post
    Given
    f() + g() * h();
    The only thing the operator precedence table tells you is that * happens before +, and that's all.
    In which order the 3 functions are called is totally up to the compiler (evaluating the operands).

    On a register-rich machine, you could imagine f() being called first, but on a register-poor machine, perhaps it's called last.
    But giving an example using functions instead of values is a little off topic since functions aren't mentioned in the Operator Precedence rules.

    Using an example that uses only variables & operators like this helps explain things better:
    Code:
    int x = 0;
    y = ++x / x++;
    My previous line of thinking was that since ++ has a higher precedence than /, x would be incremented (so x on both sides now equals 1), then it would do y = 1 / 1, then increment x again.
    But since that would be confusing for developers and harder to optimize, I guess that's why they just threw their hands up and said "ah screw it, lets just make it undefined".

  11. #26
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    The C/C++ standard has made a lot of behaviour undefined to make it work on as many platforms as possible and allow for optimizations =)
    I remember reading bjarne would like to redo C++ and get rid of all that undefined if he could, but that would break literally every program out there, so that's something that was a big no-no
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  12. #27
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    But since that would be confusing for developers and harder to optimize, I guess that's why they just threw their hands up and said "ah screw it, lets just make it undefined".
    Erm, well, not quite like that. The reason that we have sequence points is to force you to be explicit about evaluation order. C is an imperative language, which means that computing an accurate result involves narrowing the number of possible outcomes.

    a[i] = i++;
    That's a statement that has at least a couple of outcomes for i. You're at the mercy of when the compiler executes the increment, before or after the assignment, because it's not exactly clear when i's state should change. That's what they mean when they say assignment has side effects: it changes the program environment.

    a[i] = i + 1;
    This one has only one outcome.

    And like Salem said, f() + g() * h() will compute a result based on precedence, but the compiler can execute the functions in any order because there are no sequence points. This may be immaterial to the calculation and it may not be.

  13. #28
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Quote Originally Posted by cpjust View Post
    But giving an example using functions instead of values is a little off topic since functions aren't mentioned in the Operator Precedence rules.
    Not really. For ++x, what if x is a class that provides a preincrement operator? Then evaluating ++x could be identical to evaluating x.foo() where foo has code that does a preincrement.
    In both cases it is a unary function that applies to the immediate object.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  14. #29
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    Quote Originally Posted by iMalc View Post
    Not really. For ++x, what if x is a class that provides a preincrement operator? Then evaluating ++x could be identical to evaluating x.foo() where foo has code that does a preincrement.
    In both cases it is a unary function that applies to the immediate object.
    But operator++() would be included in the precedence rules, since ++ is an operator, whereas SomeOtherFunc() isn't.

    I haven't had to worry about precedence much in the past because I always use lots of parenthesis to be extremely explicit about how I want things evaluated. But since I started working with this crappy code at work, I found a ton of ambiguous statements like:
    Code:
    if ( rc = SomeFunc() == ERROR_SUCCESS )
    I'm sure the person who wrote it, meant for it to mean:
    Code:
    if ( (rc = SomeFunc()) == ERROR_SUCCESS )
    but when I checked the precedence rules, I found it actually means:
    Code:
    if ( rc = (SomeFunc() == ERROR_SUCCESS) )

  15. #30
    Just Lurking Dave_Sinkula's Avatar
    Join Date
    Oct 2002
    Posts
    5,005
    Quote Originally Posted by cpjust View Post
    But operator++() would be included in the precedence rules, since ++ is an operator, whereas SomeOtherFunc() isn't.
    Most garden variety charts show the function call operator in the list of operators.
    7. It is easier to write an incorrect program than understand a correct one.
    40. There are two ways to write error-free programs; only the third one works.*

Popular pages Recent additions subscribe to a feed