i=i++, sequence points (C++)

Started by
19 comments, last by Makaan 14 years, 2 months ago

i = i++;
I'm sure we've all seen that. Assuming i is a primitive type (such as int), is the behavior undefined? I've read that it is indeed undefined since the = operator does not introduce a sequence point (*). Is this correct? I don't fully understand why, though. The post-fix ++ operator increments by 1 and returns the old value, right? And then this old value will be assigned to i, effectively nullifying the change. (*) Unless i is a non-primitive type and operator=() is overloaded, apparently. In which case it would introduce a sequence point because it is treated as a normal function, and therefore i++ would be fully evaluated before operator=() is actually called. Would any of the C++ gurus like to shed some light on this?
Advertisement
Quote:Original post by nullsquared
Assuming i is a primitive type (such as int), is the behavior undefined?

Sequence Points 3rd paragraph.

Also open up the C++ standard and go to section 5.0.4.

Also this: sequence points.
Quote:Original post by Sirisian
Quote:Original post by nullsquared
Assuming i is a primitive type (such as int), is the behavior undefined?

Sequence Points 3rd paragraph.

Also open up the C++ standard and go to section 5.0.4.

Also this: sequence points.


Yes, I've seen these sources. Thus the "I've read that it is indeed undefined [...]". Further more, did you read the part of my post involving "I don't fully understand why, though [...]"?
Your sentences "The post-fix ++ operator increments by 1 and returns the old value, right? And then this old value will be assigned to i, effectively nullifying the change." rely heavily on the assumption that the increment will take place before the assignment.

You have to forget about the semantics of the operators involved. It is obvious by looking at the code to see what the programmer intended (well, a sane programmer). But the compiler is a machine, it sees a statement that involves two assignments, neither of which has priority over the other.
Right; what I'm asking is what goes on behind the scenes. The way I see it, i++ is a single self contained operation - "increment, return old value." Obviously it's not a single instruction in assembly. Thus I want to understand how a non-overloaded operator= works such that it may cause undefined behavior in this case. How would the assembly get mixed up such that one happens before the other, considering operator= has lower precedence than operator++(int)?

If it was something like operator=(int &lhs, int rhs) and you called operator=(i, i++) then it would work fine because both parameters would be evaluated before the function is actually executed; clearly this isn't the case, however, thus my question.
Undefined behavior has nothing to do with assembly getting "mixed up". The C++ language defines sequence points that dictate when and where you may modify a variable in a given expression. If you violate these specific rules, you will have undefined behavior on your hands, regardless of what your particular compiler does with the resulting assembly.

Quote:If it was something like operator=(int &lhs, int rhs) and you called operator=(i, i++) then it would work fine because both parameters would be evaluated before the function is actually executed; clearly this isn't the case, however, thus my question.

The order of evaluation of parameters isn't specified and there is no sequence point there I believe, which would make that undefined behavior as well.
Mike Popoloski | Journal | SlimDX
Interestingly, this is the assembly that MSVC 2008 spat out in debug mode for i = i++:
	mov	eax, DWORD PTR _i$[ebp]	mov	DWORD PTR _i$[ebp], eax	mov	ecx, DWORD PTR _i$[ebp]	add	ecx, 1	mov	DWORD PTR _i$[ebp], ecx

i gets loaded into a register, immediately reassigned back to itself, reloaded, incremented and then assigned back to itself.
Quote:Original post by Mike.Popoloski
Undefined behavior has nothing to do with assembly getting "mixed up". The C++ language defines sequence points that dictate when and where you may modify a variable in a given expression. If you violate these specific rules, you will have undefined behavior on your hands, regardless of what your particular compiler does with the resulting assembly.

I understand that, I'm just trying to figure out if that's just an arbitrary decision by the standard or if there is some underlying issue/reason.

Quote:
Quote:If it was something like operator=(int &lhs, int rhs) and you called operator=(i, i++) then it would work fine because both parameters would be evaluated before the function is actually executed; clearly this isn't the case, however, thus my question.

The order of evaluation of parameters isn't specified and there is no sequence point there I believe, which would make that undefined behavior as well.

The sequence point is before the function code is executed, which means both i and i++ will be evaluated. The reference will refer to the variable i, and i++ will return a copy of the old value of i:
// pseudo codeint i = 2;operator=(i, i++);operator=(int &lhs, int rhs){    // lhs is i, which == 3    // rhs is a copy of the old value of i, which is 2    lhs = rhs; // i == 2 again}
Quote:Original post by SiCrane
Interestingly, this is the assembly that MSVC 2008 spat out in debug mode for i = i++:
	mov	eax, DWORD PTR _i$[ebp]	mov	DWORD PTR _i$[ebp], eax	mov	ecx, DWORD PTR _i$[ebp]	add	ecx, 1	mov	DWORD PTR _i$[ebp], ecx

i gets loaded into a register, immediately reassigned back to itself, reloaded, incremented and then assigned back to itself.


That's really interesting.
Quote:Original post by nullsquared
The sequence point is before the function code is executed, which means both i and i++ will be evaluated. The reference will refer to the variable i, and i++ will return a copy of the old value of i:
// pseudo codeint i = 2;operator=(i, i++);operator=(int &lhs, int rhs){    // lhs is i, which == 3    // rhs is a copy of the old value of i, which is 2    lhs = rhs; // i == 2 again}


There is no sequence point between parameters, which means you are modifying a variable without a sequence point in between, which means it is undefined behavior, no matter whether it appears to work or not.
Mike Popoloski | Journal | SlimDX

This topic is closed to new replies.

Advertisement