0

Can someone tell me what is happening behind the scenes here?

main()
{
    int z, x=5, y=-10, a=4, b=2;
    z = ++x - --y*b/a;
    printf("%d", z);
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Kapil Garg
  • 173
  • 1
  • 2
  • 8
  • 7
    What's happening is that you're losing your friends by doing weird things.... just kidding :) – Maroun Dec 08 '15 at 10:31
  • 1
    It's happening that whoever wrote such code shouldn't be allowed behind a keyboard. – Matteo Italia Dec 08 '15 at 10:32
  • 1
    who does something like that? Thats entirely superflous. – Magisch Dec 08 '15 at 10:32
  • 4
    What do *you* think happens? Have you tried splitting up the separate operations so they are indeed separate? – Some programmer dude Dec 08 '15 at 10:34
  • 3
    Read this, http://en.cppreference.com/w/c/language/operator_precedence, then add the superfluous parentheses. It will be clear then. You also have some integer division in there. – Bathsheba Dec 08 '15 at 10:35
  • also, `main()` wont compile. It has to be `int main (void)` or at the very least `int main()` – Magisch Dec 08 '15 at 10:35
  • 1
    Z is being calculated based on the values of x, y, a & b and then printed out. As to what it is trying to achieve - I have no idea! – Robbie Dee Dec 08 '15 at 10:35
  • 3
    @Magisch: on plenty of real compilers, `main()` will compile just fine. Not that you should do it in 2015, but you often can. – John Zwinck Dec 08 '15 at 10:36
  • 5
    Is `z = (++x) - ((--y) * b)/a;` really obfuscated? – haccks Dec 08 '15 at 10:37
  • 2
    @haccks yeah, it really is ;) – artm Dec 08 '15 at 10:38
  • @Magisch The definition of `main` is fine, OP just uses the implicit `int` rule, `main() { ... }` is equal to `int main() { ... }`. – fuz Dec 08 '15 at 10:38
  • @FUZxxl strictly speaking, anything less then `int main (void)` is implementation dependant. – Magisch Dec 08 '15 at 10:39
  • @Magisch No. See ISO 9899:2011 §5.1.2.2.1 ¶1: “The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined (...) or equivalent.” `main() { ... }` is equivalent to `int main(void) { ... }`. – fuz Dec 08 '15 at 10:42
  • 2
    @FUZxxl "implicit int" was removed from the C language 16 years ago. Whenever the C tag is used, always assume the latest standard unless the OP says otherwise. – Lundin Dec 08 '15 at 10:43
  • 4
    [All answers to beating-the-dead-horse "format of main" debate](http://stackoverflow.com/a/31263079/584518). Stop spamming down this question with that now, please. – Lundin Dec 08 '15 at 10:44
  • @FUZxxl that was true in C89, but not in C99 or C11 – M.M Dec 08 '15 at 10:49

4 Answers4

3

Strange way to write code man...anyway...I try...

  1. Resolves ++x and --y
  2. Resolves multiplications and divisions
  3. Resolves the remaining (plus and minus)

So...

  1. z= 6 - (-11) * 2 /4
  2. z= 6 - (-22) / 4
  3. z= 6 - (-5) (the result is truncated due to (-22) / 4 being an integer division)

I get z= 11.

The variable z is declared int so it becomes 11.

I suggest to write this line in a simpler way!! Oh...sorry for my english...

Jay
  • 9,585
  • 6
  • 49
  • 72
sentientmachine
  • 347
  • 3
  • 14
  • 2
    The division is an integer division, so the result will be truncated there. There will be no intermediate `-5.5` value, and `z` would be 11.0 even if it was declared as `double`. – interjay Dec 08 '15 at 10:46
2

This expression

z=++x - --y*b/a;

is evaluated in the following order in an abstract machine

Variable x is incremented and becomes equal to 6. Variable y is decremented and becomes equal to -11. Variable y is multiplied by variable b and the result is equal to -22. The result of the preceding operation is divided by variable a and as there is used the integer arithmetic the result is equal to -5.

At last there is subtraction of the result from variable x and the result is equal to 11.

Run the program and be sure whether I am correct.

Take into account that a particular implementation may evaluate the operands in a different order provided that the result will be the same as I described for the abstract machine.

According to the C Standard (5.1.2.3 Program execution)

4 In the abstract machine, all expressions are evaluated as specified by the semantics. An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object).

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • You are correct. Tested on ideone: https://ideone.com/iIO6vG , aswell as with gcc on windows 7. – Magisch Dec 08 '15 at 10:42
  • 1
    Nope. Please remove the “in the following order,” that's not guaranteed in most consistency models supported by C. – fuz Dec 08 '15 at 10:42
  • @FUZxxl Read this statement to its end. – Vlad from Moscow Dec 08 '15 at 10:43
  • @haccks Read what is the abstract machine. – Vlad from Moscow Dec 08 '15 at 10:45
  • 1
    @VladfromMoscow I did. The order in which `x` and `y` are decremented is not specified, it's not specified if these are decremented before `x - y` is evaluated either, only the result has to match. – fuz Dec 08 '15 at 10:45
  • 2
    None of those operators is a sequence point, so there is no implied order to the operations, although the result will indeed be 11. – Ian Abbott Dec 08 '15 at 10:45
  • @FUZxxl Read paragraph #9 of section 1.9 Program execution of the C+= Standard. – Vlad from Moscow Dec 08 '15 at 10:50
  • @VladfromMoscow; I know what is an abstract machine. I you are thinking from this point of view then you must have to mention the rules of this machine, which I think is of bit complex for this simple question. – haccks Dec 08 '15 at 10:50
  • @haccks It is enough that i explicitly mentioned the abstract machine. – Vlad from Moscow Dec 08 '15 at 10:51
  • We had Vlad the impaler and now we have Vlad the compiler... * gets coat * – Robbie Dee Dec 08 '15 at 10:53
  • @VladfromMoscow What's C+=? – fuz Dec 08 '15 at 10:56
  • @FUZxxl It is the advanced C++.:) – Vlad from Moscow Dec 08 '15 at 10:57
  • @VladfromMoscow OPs code does *not* execute in the way your abstract machine description specifies, specifically, the order in which `x`, `y`, and `z` are assigned to is unspecified and the different order could be observed. – fuz Dec 08 '15 at 10:57
  • @FUZxxl I already explained all specially for you. Please reread my answers. – Vlad from Moscow Dec 08 '15 at 10:58
  • @VladfromMoscow I did. I still can't follow your argument. – fuz Dec 08 '15 at 10:59
  • @FUZxxl Did you already read the p. #9 I referenced to? There is an example and its description. – Vlad from Moscow Dec 08 '15 at 11:00
  • @VladfromMoscow; Your last edit makes much sense now. Agreed with you. – haccks Dec 08 '15 at 11:01
  • @FUZxxl by "observed" you mean via some implementation detail such as checking the assembly output (there is no difference in *observable behaviour*) – M.M Dec 08 '15 at 11:01
  • @FUZxxl The observed behaviour is the behaviour I described in my answer. The result shall be the same independent on how the implementation evaluates it. – Vlad from Moscow Dec 08 '15 at 11:03
  • The C++ standard is not a normative source for the C language – M.M Dec 08 '15 at 11:04
  • @VladfromMoscow I'm sorry, but OP is talking about C, I don't see how C+= is relevant to that and I don't know where the C+= standard is. – fuz Dec 08 '15 at 11:07
  • @VladfromMoscow The difference could be observed, an interrupt could occur in the middle of the statement, or a concurrent observer could observe this from the outside. – fuz Dec 08 '15 at 11:08
  • @FUZxxl; Believe me this discussion has no fruitful results. – haccks Dec 08 '15 at 11:10
  • @FUZxxl The notion of the abstract machine also is present in the C Standard. So relative to this question there is no difference. – Vlad from Moscow Dec 08 '15 at 11:11
1

First of all, please note the difference between operator precedence and order of evaluation of sub expressions.

Operator precedence dictates which operations that have to be done and evaluated, before the result of those operations are used together with the rest of the expression. This works similarly to mathematical precedence: 1 + 1 * 2 is guaranteed to give result 3, not 4. Because * has higher precedence than +.

Order of evaluation equals the actual order of execution, and is unspecified behavior, meaning that a compiler is free to execute the various sub expressions in any order it likes, in order to produce the fastest possible code. And we can't know the order. Most operators in C involve unspecified order of evaluation (except some special cases like && || ?: ,).

For example the in the case of x = y() + z(), we can know that + operation will get executed before =, but we can't tell which of the functions y and z that will get executed first. It may or may not matter to the result, depending on what the functions do.


Then to the expression in the question:

  • Operator precedence dictates that the two operations ++x and --y must be evaluated before the other operations, since the prefix unary operators have highest precedence of those present in the expression.
  • Which sub expression of ++x and --y*b/a that is evaluated first is not specified. We can't tell the order of execution (and --y*b/a does in turn contain several sub expressions). At any rate, the order of evaluation does not matter here, it will not affect the result.
  • The increments/decrements ++x and --y will take place before the results of those operations are used together with the rest of the expression.
  • Operator precedence then dictates that the operations involving * and / must be evaluated next. These operators have the same precedence, but they belong to the multiplicative operators group, which has left-to-right associativity, meaning that --y*b/a is guaranteed to evaluate --y*b first. After that, the result will get divided by a.
  • So the whole right-most sub expression is equivalent to ( (--y) * b ) / a.
  • Next, operator precedence dictates that - has higher precedence than =. So the result of the sub expressions ++x is subtracted by the result of the sub expression --y*b/a .
  • And finally the result is assigned to z, since = had the lowest precedence.

EDIT

Btw, the proper way to write the same, and get the very same machine code, is this:

++x;
--y;
z = x - (y*b)/a;

Apart from giving reduced readability, the ++ and -- operators are dangerous to mix with other operators since they contain a side effect. Having more than one side effect per expression could easily lead to various forms of unsequenced processing, which is always a bug, possibly severe. See this for examples.

Community
  • 1
  • 1
Lundin
  • 195,001
  • 40
  • 254
  • 396
0

"Operator precedence" means the rules for deciding what the operands are of each operator. In your case, using parentheses to indicate:

z=++x - --y*b/a;

is equivalent to:

z = ((++x) - (((--y) * b) / a));

Now, this line of code and the following printf statement has the same observable behaviour as the code:

z = (x + 1) - ((y - 1) * b / a);
printf("%d\n", z);
x = x + 1;
y = y - 1;

C is defined in terms of observable behaviour (which approximately means the output generated by the program; you can see a technical definition by reading the C standard). Any two programs which would produce the same observable behaviour according to the standard , are considered to be exactly equivalent.

This is sometimes called the "as-if rule" and it is this rule that allows optimization to occur.


Addressing points raised by some of the other answers: There are rules surrounding exactly what ++ and -- do. Specifically, the effects of incremeting x and decrementing y are defined so that the writing back of the increment and decrement could happen at any time during the execution of z=++x - --y*b/a; . They could be in either order, or simultaneous; the writing of the decrement could be either before or after the computation of (y-1) * b, and so on.

In some different code examples, we would use these rules to work out the observable behaviour of the program, and it would not be quite so flexible as this particular program.

But in this code example, since nothing else depends on the timing of those increments and decrements, it turns out that we can even hoist them past the printf, according to the "as-if rule".

M.M
  • 138,810
  • 21
  • 208
  • 365