Почему при использовании этой составной формы происходит сбой замены значений с помощью XOR?
Я нашел этот код для замены двух чисел без использования третьей переменной, используя XOR ^
оператора.
код:
int i = 25;
int j = 36;
j ^= i;
i ^= j;
j ^= i;
Console.WriteLine("i:" + i + " j:" + j);
//numbers Swapped correctly
//Output: i:36 j:25
Теперь я изменил приведенный выше код на этот эквивалентный код.
Мой Код:
int i = 25;
int j = 36;
j ^= i ^= j ^= i; // I have changed to this equivalent (???).
Console.WriteLine("i:" + i + " j:" + j);
//Not Swapped correctly
//Output: i:36 j:0
Теперь, я хочу знать, почему мой код дает неверный результат?
4 ответа:
EDIT: хорошо, понял.
первое, что нужно сделать, это то, что, очевидно, вы не должны использовать этот код в любом случае. Однако, когда вы расширяете его, он становится эквивалентным:
j = j ^ (i = i ^ (j = j ^ i));
(если бы мы использовали более сложное выражение, например:
foo.bar++ ^= i
важно, что++
был оценен только один раз, но здесь я считаю, что это проще.)Итак, порядок вычисления операндов всегда слева направо, поэтому для начала мы получить:
j = 36 ^ (i = i ^ (j = j ^ i));
это (выше) является наиболее важным шагом. мы закончили с 36 в качестве LHS для операции XOR, которая выполняется последней. LHS-это не " значение
j
после оценки RHS".оценка RHS ^ включает выражение "вложенный один уровень", поэтому оно становится:
j = 36 ^ (i = 25 ^ (j = j ^ i));
затем, глядя на самый глубокий уровень вложенности, мы можем заменить оба
i
иj
:j = 36 ^ (i = 25 ^ (j = 25 ^ 36));
... который становится
j = 36 ^ (i = 25 ^ (j = 61));
задание
j
в RHS происходит сначала, но результат затем перезаписывается в конце в любом случае, поэтому мы можем игнорировать это - нет никаких дальнейших оценокj
перед окончательным заданием:j = 36 ^ (i = 25 ^ 61);
теперь это эквивалентно:
i = 25 ^ 61; j = 36 ^ (i = 25 ^ 61);
или:
i = 36; j = 36 ^ 36;
что будет:
i = 36; j = 0;
я думаю вот и все правильно, и он получает правильный ответ... извиняюсь перед Эриком Липпертом, если некоторые детали о порядке оценки немного выключены : (
проверил сгенерированный IL и он выдает разные результаты;
правильный своп генерирует простой:
IL_0001: ldc.i4.s 25 IL_0003: stloc.0 //create a integer variable 25 at position 0 IL_0004: ldc.i4.s 36 IL_0006: stloc.1 //create a integer variable 36 at position 1 IL_0007: ldloc.1 //push variable at position 1 [36] IL_0008: ldloc.0 //push variable at position 0 [25] IL_0009: xor IL_000a: stloc.1 //store result in location 1 [61] IL_000b: ldloc.0 //push 25 IL_000c: ldloc.1 //push 61 IL_000d: xor IL_000e: stloc.0 //store result in location 0 [36] IL_000f: ldloc.1 //push 61 IL_0010: ldloc.0 //push 36 IL_0011: xor IL_0012: stloc.1 //store result in location 1 [25]
неверный своп генерирует этот код:
IL_0001: ldc.i4.s 25 IL_0003: stloc.0 //create a integer variable 25 at position 0 IL_0004: ldc.i4.s 36 IL_0006: stloc.1 //create a integer variable 36 at position 1 IL_0007: ldloc.1 //push 36 on stack (stack is 36) IL_0008: ldloc.0 //push 25 on stack (stack is 36-25) IL_0009: ldloc.1 //push 36 on stack (stack is 36-25-36) IL_000a: ldloc.0 //push 25 on stack (stack is 36-25-36-25) IL_000b: xor //stack is 36-25-61 IL_000c: dup //stack is 36-25-61-61 IL_000d: stloc.1 //store 61 into position 1, stack is 36-25-61 IL_000e: xor //stack is 36-36 IL_000f: dup //stack is 36-36-36 IL_0010: stloc.0 //store 36 into positon 0, stack is 36-36 IL_0011: xor //stack is 0, as the original 36 (instead of the new 61) is xor-ed) IL_0012: stloc.1 //store 0 into position 1
C # loads
j
,i
,j
,i
в стеке, и хранит каждыйXOR
результат без обновления стека, поэтому левыйXOR
использует начальное значениеj
.
переписывание:
j ^= i; i ^= j; j ^= i;
расширения
^=
:j = j ^ i; i = j ^ i; j = j ^ i;
заменить:
j = j ^ i; j = j ^ (i = j ^ i);
заменить это работает только если / потому что левая часть оператора ^ вычисляется первой:
j = (j = j ^ i) ^ (i = i ^ j);
коллапс
^
:j = (j ^= i) ^ (i ^= j);
симметрично:
i = (i ^= j) ^ (j ^= i);