programing

이 복합 양식을 사용할 때 XOR로 값을 바꾸는 데 실패하는 이유는 무엇입니까?

nasanasas 2020. 10. 17. 10:36
반응형

이 복합 양식을 사용할 때 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

이제 알고 싶습니다. 왜 내 코드가 잘못된 출력을 제공합니까?


편집 : 좋아, 알았다.

첫 번째 요점은 어쨌든이 코드를 사용하지 말아야한다는 것입니다. 그러나 확장하면 다음과 동일하게됩니다.

j = j ^ (i = i ^ (j = j ^ i));

(같은 좀 더 복잡한 표현을 사용 한다면는 한 번만 평가 foo.bar++ ^= i되는 것이 중요 ++하지만 여기서는 더 간단하다고 생각합니다.)

이제 피연산자의 평가 순서는 항상 왼쪽에서 오른쪽이므로 시작하려면 다음을 얻습니다.

j = 36 ^ (i = i ^ (j = j ^ i));

이것은 (위) 가장 중요한 단계입니다. 마지막으로 실행되는 XOR 연산의 LHS는 36으로 끝났습니다. LHS는 " jRHS가 평가 된 후의 값"이 아닙니다 .

^의 RHS 평가에는 "1 단계 중첩"표현식이 포함되므로 다음과 같이됩니다.

j = 36 ^ (i = 25 ^ (j = j ^ i));

그런 다음 가장 깊은 수준의 중첩을 살펴보면 ij다음을 모두 대체 할 수 있습니다 .

j = 36 ^ (i = 25 ^ (j = 25 ^ 36));

...된다

j = 36 ^ (i = 25 ^ (j = 61));

jRHS 의 할당 이 먼저 발생하지만 결과는 어쨌든 마지막에 덮어 쓰기되므로 무시할 수 있습니다 j. 최종 할당 전에는 더 이상의 평가가 없습니다 .

j = 36 ^ (i = 25 ^ 61);

이제 다음과 같습니다.

i = 25 ^ 61;
j = 36 ^ (i = 25 ^ 61);

또는:

i = 36;
j = 36 ^ 36;

다음과 같이됩니다.

i = 36;
j = 0;

I think that's all correct, and it gets to the right answer... apologies to Eric Lippert if some of the details about evaluation order are slightly off :(


Checked the generated IL and it gives out different results;

The correct swap generates a straightforward:

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]

The incorrect swap generates this code:

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

It's evident that the code generated in the second method is incorect, as the old value of j is used in a calculation where the new value is required.


C# loads j, i, j, i on the stack, and stores each XOR result without updating the stack, so the leftmost XOR uses the initial value for j.


Rewriting:

j ^= i;       
i ^= j;
j ^= i;

Expanding ^=:

j = j ^ i;       
i = j ^ i;
j = j ^ i;

Substitute:

j = j ^ i;       
j = j ^ (i = j ^ i);

Substitute this only works if/because the left hand side of the ^ operator is evaluated first:

j = (j = j ^ i) ^ (i = i ^ j);

Collapse ^:

j = (j ^= i) ^ (i ^= j);

Symmetrically:

i = (i ^= j) ^ (j ^= i);

참고URL : https://stackoverflow.com/questions/5577140/why-does-swapping-values-with-xor-fail-when-using-this-compound-form

반응형