이 복합 양식을 사용할 때 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는 " j
RHS가 평가 된 후의 값"이 아닙니다 .
^의 RHS 평가에는 "1 단계 중첩"표현식이 포함되므로 다음과 같이됩니다.
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;
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);
'programing' 카테고리의 다른 글
git EOL 변환 비활성화 (0) | 2020.10.17 |
---|---|
요소 / 문서에 연결된 JavaScript 이벤트 리스너 / 핸들러가 있는지 확인하는 방법은 무엇입니까? (0) | 2020.10.17 |
C #에서 System.Type과 System.RuntimeType의 차이점은 무엇입니까? (0) | 2020.10.17 |
Java 7에서 Java 8보다 StringBuilder # append (int)가 더 빠른 이유는 무엇입니까? (0) | 2020.10.17 |
단일 하위 폴더의 git-status를 얻는 방법은 무엇입니까? (0) | 2020.10.17 |