3

I want to exchange the values pointed to by int *x and by int *y using the expression

*x ^= *y ^= *x ^= *y;

(Well,I know this expression is awkward, and I just want to know the difference, no offense.) This worked in C++, but failed in C. However if I divide it into three parts, like below

*x ^= *y;
*y ^= *x;
*x ^= *y;

It works fine for both languages.

So, what are the difference of the operator ^= in C and C++?

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
吴俣铖
  • 61
  • 6
  • 6
    **−1** I stopped reading at "*x ^= *y ^= *x ^= *y;". If you're going to ask a question, present code that's less repulsive. Thanks. – Cheers and hth. - Alf Jul 17 '16 at 13:23
  • 2
    @Cheersandhth.-Alf I'm afraid this repulsive line is what the question is about in the end. – Baum mit Augen Jul 17 '16 at 13:32
  • 2
    Not sure why the downvotes? The question provides working code that reproduces the error. Not many questioners do that. It also exposes an interesting difference between `C` and `C++` that could bite other people. Do we really down-vote due to code snobbery now? – Galik Jul 17 '16 at 13:41
  • @Galik: The original posting, as it was at the time of your comment, was nastier, including two versions of a program, for C and C++, where the only difference was just as nasty, namely that the C version had an invalid `void main`. It was almost as if designed for trolling. Still, I downvoted for the lack of a clear example and for dumping long reams of irrelevant code, not for the motivations you assume (discussing motivations is, by the way, also what trolls generally do). – Cheers and hth. - Alf Jul 17 '16 at 14:11
  • Oh, and the question itself changed completely in the edit, originally about pointers, "So, what are the difference of a pointer(*) between c and c++", and now it's about `^=`. – Cheers and hth. - Alf Jul 17 '16 at 14:19
  • If you my edit with the `int*` conflicts with your intend, feel free to change that back. The difference between built-in types like `int` and user defined types is crucial in the light of C++ operator overloading. (I'm confident the edit is fine though, as this sort of swapping does not work for general types.) – Baum mit Augen Jul 17 '16 at 14:33
  • 1
    You should also consider adding the actual difference between C and C++ you observed, including the examples, to the question again. – Baum mit Augen Jul 17 '16 at 14:35
  • 1
    @Galik because this is a frequently-asked question. Just search for "xor swap". Example: [Sequence Point - Xor Swap get wrong result](http://stackoverflow.com/questions/9958514/sequence-point-xor-swap-on-array-get-wrong-result) – Raymond Chen Jul 17 '16 at 14:36
  • @RaymondChen I don't see how this explains the difference in behavior between C and C++. – Baum mit Augen Jul 17 '16 at 15:05
  • 1
    It is undefined behavior which means "anything can happen." – Raymond Chen Jul 17 '16 at 15:12
  • @RaymondChen But it's not UB in C++ since 5 years ago. – Baum mit Augen Jul 17 '16 at 15:12
  • 2
    It is UB in C, which was the question. "Why doesn't this work in C?" – Raymond Chen Jul 17 '16 at 15:13
  • @Raymond Chen I have read that question. Actually it help me a lot to understand things like _sequence point_, _side effect_ and so on. But his focus was on the swapping, and I focus the difference between c and c++. So, I think they may not exact the same, even it has turned out the reason behind the two problems is no difference. – 吴俣铖 Jul 18 '16 at 04:58
  • @Baum mit Augen Well, I believe the edit is as you said fine enough, and the actual difference I observed was also mentioned in there, which is _This worked in C++, but failed in C_. I was wondering if there is a need to change the title to _Difference of the order of the evaluation of operands between C and in C++ regarding the “^=” operator_ so that it will be a more specific question. – 吴俣铖 Jul 18 '16 at 05:00
  • @吴俣铖 I don't think that needs to be in the question, it's enough to have that in the answer. – Baum mit Augen Jul 20 '16 at 22:18

3 Answers3

10

The difference is not in the pointers as you initially suspected, but in the different order of evaluation rules. In the "new" C++11 "Sequenced-before rules", we have:

The side effect (modification of the left argument) of the built-in assignment operator and of all built-in compound assignment operators is sequenced after the value computation (but not the side effects) of both left and right arguments, and is sequenced before the value computation of the assignment expression (that is, before returning the reference to the modified object)

(From cppr.) This rule guarantees the desired right-to-left evaluation of your expression.

In contrast to this, C and C++98 use "Sequence points". Since there are no sequence points in the long statement, you have multiple unsequenced modification of the values the pointers point to and thus invoke Undefined Behavior.

For C, gcc warns about this (live). For C++98 it apparently uses the new rules already, which is fine because undefined behavior is undefined.

Splitting the statement solves this problem of course because the end of the statement explicitly introduces sequence points where you need them. It is also superior because it is more readable and one does not need to know sequencing rules to determine whether or not the code is correct.


For reference: A great explanation of sequencing rules in C++ can be found here.

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
  • Well spotted, +1. But note that this only answers the current question, not the original one about pointers. – Cheers and hth. - Alf Jul 17 '16 at 14:14
  • It could be better if you can provide more detailed annotations to standard of _Sequence points_ and _side effect_ so it might be helpful for beginners like me to read more about this. Here is a related post: [Undefined behavior and sequence points](http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points) – 吴俣铖 Jul 18 '16 at 04:52
  • @吴俣铖 I don't think a complete explanation of sequencing is in the scope of this answer, and I dislike incomplete explanations. So I'll include the link from your comment as reference, if you find a good link for C, feel free to add that as well. – Baum mit Augen Jul 20 '16 at 22:17
3

It is undefined behaviour to modify the same variable more than once in a single statement, so the compiler is allowed to do anything when you do *x ^= *y ^= *x ^= *y. It's the same reason why ++i + i++ or the like is always wrong.

N. Shead
  • 3,828
  • 1
  • 16
  • 22
  • 1
    A side note `++i + i++ or the like is always wrong` is bogus It is is UB as per the standard – sjsam Jul 17 '16 at 13:53
  • 2
    I meant that it is never what you intended to do. I can see how I was misinterpreted, though. – N. Shead Jul 17 '16 at 13:54
  • 2
    Btw, *"It is undefined behaviour to modify the same variable more than once in a single statement"* isn't even true in C and C++98, `++x, ++x;` is a single statement, but not UB because the comma introduces a sequence point. – Baum mit Augen Jul 17 '16 at 19:39
2

To answer the stated question: There's no difference for raw pointers between C and C++.

But I think your real question is something else...

Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70
  • 1
    Upvoted because this is the only answer that addresses *the original question*. – Cheers and hth. - Alf Jul 17 '16 at 14:18
  • Your assumption was right, at first I thought it was a pointer problem, after reading the answers I found it was more likely related to _the order of evaluation_, or something called _sequence point_ and _side effect_ which I had little known. – 吴俣铖 Jul 18 '16 at 05:19