What is the operator precedence/order of evaluation in this complex expression, and why is the result -129?

185 Views Asked by At

I don't understand in which order actions take place in C++ expressions.

For example: Why do we get here -129? Can you explain order of actions?

#include <stdio.h>

int main()
{
    char a = 60;
    unsigned c = 88;
    long d = 134;
    int e = -6;

    printf("Reuslt: %ld\n",!a++<sizeof(long double)&(c)|~d--+-e );
    
    return 0;
}

I tried to do this with actions using int k1, k2, k3, ..., kn(where k stands for 1 action) , but i don't understand order of actions yet.

3

There are 3 best solutions below

0
Lajos Arpad On

I will not provide you the exact answer because this is an exercise and ultimately it is you who needs to solve it. But I will give you the information you need in order to solve it.

Priorities:

  • !: 3
  • a++: 2
  • <: 9
  • sizeof(): 3
  • a&b: 11
  • |: 13
  • ~: 3
  • d--: 2
  • +: 6
  • -e: 3

Source: https://en.cppreference.com/w/cpp/language/operator_precedence

One would expect the order to be left-to-right when we speak about operations of the same priority (except special cases, like assignment, which is not present here and where we go right-to-left), an order overriden by the priorities of the operations. The lower the priority number, the higher the priority of the operation is.

For example, !a++ will execute the ++ first and only then the !, because ++ is of higher priority (lower prio number means a preceding operator). Therefore, you are well-advised to take a pen & paper and draw the expression tree.

This will allow you to understand what exactly happens here and in what order. Try to figure this out without peeking at the actual result of the expression and compare the result you get without being influenced with the result you test later using the expression. If the results are matching, then it's likely (but not 100% proven) that you were correct. If the results do not match, then something's off.

If the results are mismatching, then isolate subtrees of the expression tree and evaluate them separately until you get the exact reason(s) that caused the mismatch and then you will understand why you were wrong.

0
Ted Lyngmo On

You have ...

!a++ < sizeof(long double) & (c) | ~d-- + -e

... so let's take what we need from the C++ Operator Precedence table and add parentheses to clarify how to interpret the expression:

Precedence Operator Description Associativity
2 a++ a-- Suffix/postfix increment and decrement Left-to-right
3 +a -a Unary plus and minus Right-to-left
! ~ Logical NOT and bitwise NOT
sizeof Size-of
6 a+b a-b Addition and subtraction Left-to-right
9 < <= > >= For relational operators < and ≤ and > and ≥ respectively -"-
11 a&b Bitwise AND -"-
13 | Bitwise OR (inclusive or) -"-
  • 2. For a++ and d--:
    !(a++) < sizeof(long double) & (c) | ~(d--) + -e
    
  • 3. For !(a++), sizeof, ~(d--) and -e (I'm omitting parentheses around sizeof(type) here):
    (!(a++)) < sizeof(long double) & (c) | (~(d--)) + (-e)
    
  • 6. For (~(d--)) + (-e):
    (!(a++)) < sizeof(long double) & (c) | ((~(d--)) + (-e))
    
  • 9. For (!(a++)) < sizeof(long double):
    ((!(a++)) < sizeof(long double)) & (c) | ((~(d--)) + (-e))
    
  • 11. For ((!(a++)) < sizeof(long double)) & (c):
    (((!(a++)) < sizeof(long double)) & (c)) | ((~(d--)) + (-e))
    
  • 13. There's only | left at this point so these go around the whole expression:
    ((((!(a++)) < sizeof(long double)) & (c)) | ((~(d--)) + (-e)))
    

And there is the expression with parentheses to make it clearer what sub-expressions that ties together and you now only have to untangle the mess from the inner parentheses and out.

2
Olaf Dietsche On

Starting from @TedLyngmo's answer

  • since a and d isn't used later in the code, you may ignore and remove the post increment and decrement operators

    ((((!(a)) < sizeof(long double)) & (c)) | ((~(d)) + (-e)))
    
  • !a is 0

    ((((0) < sizeof(long double)) & (c)) | ((~(d)) + (-e)))
    
  • 0 < sizeof(long double) gives true or 1

    (((1) & (c)) | ((~(d)) + (-e)))
    
  • c is an even number, so 1 & c results in 0

    ((0) | ((~(d)) + (-e)))
    
  • Bitwise 0 | anything remains anything, so it may be dropped

    (~(d)) + (-e)
    
  • +(-e) is basic arithmetic

    (~(d)) - e
    
  • These were just simple actions, reducing the expression to its minimum. The remaining expression is just (~d) - e or (~134) + 6, consisting of a difference (or sum) and a bitwise not. For this, you need an understanding of binary arithmetic and Two's complement (Wikipedia).


  • Thanks to @user17732522's comment: there's a twist in the bitwise or: 0 | anything, see also Implicit conversions (cppreference).

    • Since the operands have types unsigned int and signed long, the first operand (0) will be converted to signed long and the result is as described in the question.
    • If the operands were unsigned int and signed int instead, the second operand ((~d) - e) would have been converted to unsigned int and the result different.