tags:

views:

345

answers:

8

Take the following C code (K&R pg. 77) :

push(pop() - pop()); /* WRONG */

The book says that since - and / are not commutative operators, the order in which the 2 pop functions are evaluated is necessary (obviously, to get the correct result)...and thus you have to put the result of the first function in a variable first and then proceed with the arithmetic, like such:

op2 = pop();
push(op2 - pop());

Apparently this is because the compiler can't guarantee in which order the functions are evaluated (...why?)


My question is, does C# do this same thing? as in, do I have to worry about this sort of thing when working with C# ? and for that matter, any of the other higher level languages ?

+8  A: 

In C# it is left to right: http://blogs.msdn.com/oldnewthing/archive/2007/08/14/4374222.aspx


Re: C++ order

Apparently this is because the compiler can't guarantee in which order the functions are evaluated (...why?)

Any particular compiler can guarantee the order. The problem is that the language spec does not specify an order, so each compiler can do whatever it wants. This means you have to add a sequence point between the two method calls if you want to guarantee ordering.

Michael Donohue
+1  A: 

Order of evaluation is well-defined in C# in all cases, and is left-to-right. From C# language spec:

The order of evaluation of operators in an expression is determined by the precedence and associativity of the operators (§7.2.1). Operands in an expression are evaluated from left to right. For example, in F(i) + G(i++) * H(i), method F is called using the old value of i, then method G is called with the old value of i, and, finally, method H is called with the new value of i. This is separate from and unrelated to operator precedence

In case of C++, it's not that the order couldn't be defined; it's that allowing the order to be undefined allows the compiler to better optimize code.

Pavel Minaev
A: 

I believe in C# the argument list is evaluated in order, from left to right.

nagul
+2  A: 

From Fabulous Adventures In Coding: Precedence vs Associativity vs Order:

Another way to look at it is that the rule in C# is not "do the parentheses first", but rather to parenthesize everything then recursively apply the rule "evaluate the left side, then evaluate the right side, then perform the operation".

280Z28
A: 

Colour me surprised, but apparently C# does the "right" thing, and evaluates left to right:

void Main()
{
    Console.WriteLine(Pop() - Pop()); // Prints -1
}

// Define other methods and classes here
bool switchVar = true;
int Pop()
{
    int ret;
    if (switchVar)
     ret = 1;
    else
     ret = 2;
    switchVar = !switchVar;

    return ret;
}
Matthew Scharley
While you happen to be right in this case, don't fall into the trap of tests that show 'it works for me' to answer these types of questions - it's quite possible that a C compiler would give the same result, but you would be incorrect to assume that meant it **had** to give that result.
Michael Burr
Considering there are only really two C# compilers (Microsoft's csc, and mono), it's not such a big deal though, compared the veritable pleathora of C/C++ compilers.
Matthew Scharley
@Matthew Scharley: First no: there are at least three C# compilers I am aware of (dotGNU). Second no: The number of C# compilers being very limited, and for them something being true, that something-being-true is only a test, not a proof. I fully agree with Michael Burr, "works for me" is void, even if it is not a big deal in this case.
phresnel
+2  A: 

No, C# doesn't do the same thing. It doesn't use evaluation boundaries the same way as C does, where the order of execution between the boundaries is undefined. The expression is evaluated left to right, just as you would expect.

If you are ever uncertain of the order of execution, or if you want to make the code clearer, you should use a local variable for an intermediate result. Local variables are very cheap to allocate as they are allocated on the stack, and in some cases the compiler is even able to put the variable in a register so that it's not allocated at all. Besides, depending on how the expression looks the compiler may need to use a local variable to store the intermediade result anyway.

Guffa
+2  A: 

To answer your question about why C doesn't define the order of operation, it's simply because the inventors of C decided it would be valuable to give compiler implementors the opportunity to optimize expression evaluation. They also decided that it was more valuable than giving programmers certainty in expression evaluation.

Remember that when C was originally developed, machines were much less capable than today, and there was more interest in giving compilers the optimization leeway. Today, there is often more weight given to safer, more predicatble code.

Michael Burr
Along with the shift of importance between performance vs predictability (which has a lot to do with the shift of the major cost from computer operation cost to programmer's salary), one has also remarked that the cases where it made a meaningful difference were already allowed because the compiler could easily proof there was no behavioral differences.
AProgrammer
A: 

The book says that since - and / are not commutative operators, the order in which the 2 pop functions are evaluated is necessary (obviously, to get the correct result)...and thus you have to put the result of the first function in a variable first and then proceed with the arithmetic.

That's not quite correct. K&R allowed rearrangement of commutative operators (done away with in ANSI C). Since suibtraction is not commutative, it is not re-arrangeable...under that rule, at least.

(Un)fortunately, C also doesn't define the order of evaluation (outside of a fairly small scope) - which means the compiler can call those functions in any order (as long as the result of pop() - pop() is fully evaluated before calling push())

Which - in this case, results in the same problem - but for a different reason.

Mark Brackett