Потенциальная проблема в «Обмен значениями двух переменных без использования третьей переменной» - PullRequest
19 голосов
/ 18 сентября 2010

Недавно я пришел к этому методу для обмена значениями двух переменных без использования третьей переменной.

a^=b^=a^=b

Но когда я попробовал приведенный выше код на разных компиляторах, я получил разные результаты, некоторые дали правильные результаты, некоторые нет.

Что-то ужасно не так с кодом?

Ответы [ 8 ]

39 голосов
/ 18 сентября 2010

Что-то ужасно не так с кодом?

Да!

a^=b^=a^=b фактически вызывает неопределенное поведение в C и C ++, потому что вы пытаетесь изменить значение a более одного раза между двумя точками последовательности.


Попробуйте написать (хотя и не надежно)

a ^= b;
b ^= a;
a ^= b;

вместо a^=b^=a^=b.

P.S : Никогда не пытайтесь менять значения двух переменных без использования третьей. Всегда используйте третью переменную.

РЕДАКТИРОВАТЬ :

Как @ caf заметил, что b^=a^=b в порядке, хотя порядок вычисления аргументов оператора ^= не указан, так как все обращения b в выражении используются для вычисления последнее значение, которое хранится в b, поведение четко определено.

16 голосов
/ 18 сентября 2010

Если вы используете C ++, почему бы не использовать алгоритм подкачки в STL?Он идеально подходит для этой цели и очень ясно, что он делает:

#include <algorithm>
using namespace std;

// ...

int x=5, y=10;    // x:5 y:10
swap(x,y);        // x:10 y:5
5 голосов
/ 18 сентября 2010

Я предлагаю вам использовать std :: swap () для c ++.

Для c используйте этот макрос. Обратите внимание, что сначала вам нужно сравнить a и b, иначе, когда они указывают на одну и ту же ячейку памяти, вы стираете значение, и оно становится равным 0.

#define swap(a, b)  ((a) == (b) || (a) ^= (b), (b) ^= (a), (a) ^= (b))
5 голосов
/ 18 сентября 2010

Основано на материалах R. & sellibitze:

Используйте оператор запятой:

 (a^=b,b^=a,a^=b);

Из текста и Википедии:

"Оператор запятой можно использовать для связывания связанных выражений вместе. Связанный с запятыми список выражений вычисляется слева направо, а значение самого правого выражения является значением объединенного выражения.. Он действует как точка последовательности. "

" Точка последовательности гарантирует, что все побочные эффекты предыдущих оценок будут выполнены, и никаких побочных эффектов от последующих оценок еще не было выполнено.Он удаляет неопределенное поведение, возникающее из-за неясного порядка выполнения исходного выражения. "

4 голосов
/ 18 сентября 2010

Сделай так:

a ^= b;
b ^= a;
a ^= b;
1 голос
/ 08 января 2011

А как насчет этого?

a = a + b;
b = a - b;
a = a - b;
0 голосов
/ 19 октября 2013

Вы также можете попробовать следующий, но если числа достаточно велики, значение будет переполнено

a=a*b;
b=a/b;
a=a/b;
0 голосов
/ 10 мая 2012

Я удивился, почему никто не предложил заключить в скобки это выражение. Кажется, это уже не UB.

a^=(b^=(a^=b));
...