0

My out-of-the-box Xcode C compiler and Chrome Console(Javascript interpreter) both fail to increment, resulting in 3.

snippet from my C:

int a = 3;
a = a++;
printf("%d\n",a);

I've read that the problem with such code for C/C++ [compilers] has to do with multiple modifications between sequence points. Does this concept apply to other common language implementations?

user2864740
  • 60,010
  • 15
  • 145
  • 220
frogleaf
  • 43
  • 6

4 Answers4

2

In C/C++

a = a++;
a = ++a  

both will invokes undefined behavior.

The C Standard states that:

Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored.

The behavior changes as the rules for order of evaluation changes in different languages.
In case of C#, the order of evaluation rule (the order in which each operand in an expression is evaluated) is:

"evaluate each subexpression strictly left to right".

This means that both of the above expression is valid. In C#, i++ = ++i always guaranteed to be false while ++i = i++ be true. The same is true for Java.

In Python there is no ++ or -- operator, so do not think about that :).

haccks
  • 104,019
  • 25
  • 176
  • 264
1

Each language is free to define its own rules. You mentioned C, and in C, i = i++ is undefined behavior. This allows compiler writers to either not support this expression, or to decide how it should be translated. People will run to their compiler, test a program, then post an answer based on their particular compiler. Most compiler writers prefer to compile the expression in some sane way and may even document how their particular implementation defines the Undefined Behaviour (UB), but only applies for that implementation, and maybe even that version. The only way to use features classified as UB is to avoid them; the syntax is practically not legal, even though it compiles.

C#, on the other hand, chose to define the behavior, and handles it the way I feel is natural for a stack based implementation (of course there are other views on what is natural and intuitive, hence the variance across languages) though nothing says that C# has to run on the CLR or a stack based implementation.

Rather than use C#, I'll use my Cola .NET compiler which has some similarities to C#, and I chose to use the same rules for prefix and postfix increment.

If you can read stack based opcodes, here is how I implement it, and it is identical in C# as far as I know.

Assuming:

int i = 3;

Here is how the following line compiles:

i = i++;

i++ evaluates to i prior to the increment because i++ is a post increment operation. So we store the original value of i into a temporary (in .NET CLR we store it on the stack). Since the eventual assignment to left hand side will happen after everything else, and the value we will assign is "frozen" at the beginning before the increment, it doesn't matter what happens to i in the process of the increment, we will stomp on it at the end.

ldloc 'i'   //  push pre-increment value (3) to be assigned to left side
ldloc 'i'   //  push pre-increment value (3) for the increment expression
ldc.i4.1    //  push constant 1, the amount to increment by
add         //  Do the increment, add top 2 operands, resulting in 4 on stack
stloc 'i'   //  pop top of stack (4) into i, now the increment is complete and i is 4
stloc 'i'   //  pop the top of stack (3), final assignment to i on left hand side

The i of 4 is a ghost value that exists only temporarily.

It is simpler if you change the left hand side to k, but the end result in i will actually be different:

k = i++;

Since we only assign to i once, there is no lost or ghost value.

ldloc 'i'
ldloc 'i'
ldc.i4.1
add
stloc 'i'   //  pop top of stack (4) into 'i', now the increment is complete and 'i' is 4
stloc 'k'   //  pop the top of stack (3) off and assigns to 'k' on left hand side

Now k == 3 and i == 4

codenheim
  • 20,467
  • 1
  • 59
  • 80
  • +1 for showing the IL (although this works because the C# specification says this is how the IL must work, and doesn't provide leeway to compilers as C does) – user2864740 Sep 28 '14 at 04:43
  • Agreed, though I was actually showing the IL from Cola, not C#, so C#'s IL may actually differ, but the result will be the same. Just a disclaimer in case someone checks and finds a variance. – codenheim Sep 28 '14 at 04:54
0

While the same cannot be said for C/C++ (due to modification within the same sequence point),

This behavior is well-defined in some languages including Java, C#, and JavaScript. (There should really be a different question/answer to prove each of these cases..)

In these three languages (Java/C#/JS) the postfix++ operator is applied immediately to the l-value, yielding the original value, with the side-effect guaranteed to happen prior to the assignment. This is a result of guaranteeing the strict left-right evaluation of all expressions.

I believe perl has some quirks with such operators (where it may yield a reference to the variable), but wasn't able to draw any out in this particular case. Other languages will have to be analyzed on a case-by-case basis as they are, well, different languages with different rules and guarantees.

user2864740
  • 60,010
  • 15
  • 145
  • 220
-2

It will return 3 as you are storing the value before increment. Use prefix operator instead of postfix.

int a = 3;
a = ++a;
printf("%d\n",a);

Now it will increment and print 4.

user2803495
  • 25
  • 1
  • 8
  • But why isn't `a` still incremented *after* the assignment? – user2864740 Sep 28 '14 at 03:57
  • 1
    @user2864740 It increments, then assigns the former value. – Jonathan Lonowski Sep 28 '14 at 03:58
  • @JonathanLonowski What makes the increment operation "complete" prior to the assignment? – user2864740 Sep 28 '14 at 03:58
  • @JonathanLonowski My question is then: why does (or what guarantees that) a++ happen immediately, prior to the assignment, and not after the entire statement (a = a++) completes? – user2864740 Sep 28 '14 at 04:01
  • it's postfix operator and statement has no effect later after once its executed. – user2803495 Sep 28 '14 at 04:03
  • @JonathanLonowski But `a++` works on an l-value. If it were strictly based on precedence and left-right evaluation then there be no issue with similar cases and sequence points in C. That is, what (in C) *guarantees* such behavior the OP reported? – user2864740 Sep 28 '14 at 04:03
  • 1
    An answer should address the OP's concerns about sequence points and/or UB: "I've read that the problem with such code for C/C++.." If such does not apply then it should be specifically stated. – user2864740 Sep 28 '14 at 04:11