[C#] i = 0의 경우 (i + = i ++)가 0과 같은 이유는 무엇입니까?

다음 코드를 사용하십시오 (콘솔 응용 프로그램으로 사용 가능).

static void Main(string[] args)
{
    int i = 0;
    i += i++;
    Console.WriteLine(i);
    Console.ReadLine();
}

결과 i는 0입니다. (내 동료 중 일부가했던 것처럼) 2를 기대했습니다. 아마도 컴파일러 i는 0 이 되는 일종의 구조를 만듭니다 .

내가 2를 예상 한 이유는 내 생각에 오른쪽 문장이 먼저 평가되어 i가 1 씩 증가하기 때문입니다. i는 이미 1이므로 1에 1을 더하고 있습니다. 1 + 1 = 2입니다. 분명히 이것은 일어나지 않습니다.

컴파일러가 무엇을하는지 또는 런타임에 어떻게되는지 설명 할 수 있습니까? 결과가 왜 0입니까?

일부 면책 조항 : 나는이 코드를 사용하지 않아야하며 아마도 사용해서는 안된다는 것을 알고 있습니다. 나는 결코하지 않을 것입니다. 그럼에도 불구하고, 왜 그런 식으로 행동하고 정확히 무슨 일이 일어나는지 아는 것이 흥미 롭습니다.



답변

이:

int i = 0;
i += i++

당신이하는 것처럼 볼 수 있습니다 (다음은 지나치게 단순화 된 것입니다).

int i = 0;
i = i + i; // i=0 because the ++ is a postfix operator and hasn't been executed
i + 1; // Note that you are discarding the calculation result

실제로 발생하는 일은 그보다 더 관련이 있습니다. MSDN, 7.5.9 Postfix 증가 및 감소 연산자를 살펴보십시오 .

x ++ 또는 x– 형식의 접미사 증가 또는 감소 작업의 런타임 처리는 다음 단계로 구성됩니다.

  • x가 변수로 분류 된 경우 :

    • 변수를 생성하기 위해 x를 평가합니다.
    • x 값이 저장됩니다.
    • 선택된 연산자는 x의 저장된 값을 인수로 사용하여 호출됩니다.
    • 연산자가 반환 한 값은 x의 평가에 의해 주어진 위치에 저장됩니다.
    • 저장된 x 값은 작업 결과가됩니다.

때문에 유의 우선 순위 , 접미사가 ++발생 하기 전에 += , 그러나 미사용 인 것을 결과 단부 (이전 값이 i사용된다).


의보다 철저한 분해 i += i++가 구성되어 부품을 모두 알고 하나를 필요로하지 않습니다 +=++원자하지 않은 (즉, 둘 중 하나는 하나의 작업입니다) 그들이처럼 그들이 보이는 경우에도. 구현 방법에는 임시 변수, i작업을 수행하기 전의 복사본 ( 각 작업마다 하나씩)이 포함됩니다. ( 와 각각에 사용되는 임시 변수에 이름 iAdd과 이름을 사용합니다 ).iAssign+++=

따라서 발생하는 상황에 더 가까운 근사치는 다음과 같습니다.

int i = 0;
int iAdd = i; // Copy of the current value of i, for ++
int iAssign = i; // Copy of the current value of i, for +=

i = i + 1; // i++ - Happens before += due to order of precedence
i = iAdd + iAssign;


답변

실행중인 코드의 분해 :

int i = 0;
  xor         edx, edx
  mov         dword ptr i, edx         // set i = 0
i += i++;
  mov         eax, dword ptr i         // set eax = i (=0)
  mov         dword ptr tempVar1, eax  // set tempVar1 = eax (=0)
  mov         eax, dword ptr i         // set eax = 0 ( again... why??? =\ )
  mov         dword ptr tempVar2, eax  // set tempVar2 = eax (=0)
  inc         dword ptr i              // set i = i+1 (=1)
  mov         eax, dword ptr tempVar1  // set eax = tempVar1 (=0)
  add         eax, dword ptr tempVar2  // set eax = eax+tempVar2 (=0)
  mov         dword ptr i, eax         // set i = eax (=0)

동등한 코드

다음 코드와 동일한 코드로 컴파일됩니다.

int i, tempVar1, tempVar2;
i = 0;
tempVar1 = i; // created due to postfix ++ operator
tempVar2 = i; // created due to += operator
++i;
i = tempVar1 + tempVar2;

두 번째 코드의 분해 (동일 함을 증명하기 위해)

int i, tempVar1, tempVar2;
i = 0;
    xor         edx, edx
    mov         dword ptr i, edx
tempVar1 = i; // created due to postfix ++ operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar1, eax
tempVar2 = i; // created due to += operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar2, eax
++i;
    inc         dword ptr i
i = tempVar1 + tempVar2;
    mov         eax, dword ptr tempVar1
    add         eax, dword ptr tempVar2
    mov         dword ptr i, eax

분해 창 열기

대부분의 사람들은 Visual Studio 디스 어셈블리 창을 사용하여 최종 메모리 내 어셈블리 코드를 볼 수 있다는 것을 모릅니다 . 실행되고있는 머신 코드를 보여줍니다. CIL이 아닙니다.

디버깅하는 동안 이것을 사용하십시오 :

Debug (menu) -> Windows (submenu) -> Disassembly

postfix ++에서 무슨 일이 일어나고 있습니까?

postfix ++는 평가 후 피연산자의 값을 늘리고 싶다고 말합니다. 모두가 알고있는 것은 “평가 후” 의 의미입니다 .

그래서 않습니다 “평가 후” 수단 :

  • 동일한 코드 행에서 피연산자의 다른 사용법은 영향을 받아야합니다.
    • a = i++ + i 두 번째 i는 증분의 영향을받습니다
    • Func(i++, i) 두 번째 나는 영향을받습니다
  • 같은 동일 라인에 대하여 단락 연산자의 다른 용도 ||&&:
    • (false && i++ != i) || i == 0 세 번째 i는 평가되지 않기 때문에 i ++의 영향을받지 않습니다.

따라서 의미는 무엇 i += i++;입니까?

와 동일 i = i + i++;

평가 순서는 다음과 같습니다.

  1. i + i 저장 (즉, 0 + 0)
  2. 증가 i (i가 1이 됨)
  3. 1 단계의 값을 i에 지정하십시오 (i는 0이됩니다).

증분이 삭제되지 않습니다.

의 의미는 무엇입니까 i = i++ + i;?

이것은 이전 예제와 동일하지 않습니다. 세 번째 i는 증분의 영향을받습니다.

평가 순서는 다음과 같습니다.

  1. i 저장 (즉, 0)
  2. 증가 i (i가 1이 됨)
  3. 단계 1 + i의 저장 값 (즉, 0 + 1)
  4. 3 단계의 값을 i에 할당하십시오 (i는 1이됩니다).

답변

int i = 0;
i += i++;

다음과 같이 평가됩니다.

Stack<int> stack = new Stack<int>();
int i;

// int i = 0;
stack.Push(0);                   // push 0
i = stack.Pop();                 // pop 0 --> i == 0

// i += i++;
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(1);                   // push 1
i = stack.Pop() + stack.Pop();   // pop 0 and 1 --> i == 1
i = stack.Pop() + stack.Pop();   // pop 0 and 0 --> i == 0

i, i++표현식에 의해 한 번, +=명령문에 의해 한 번 두 번 변경됩니다 .

그러나 +=진술 의 피연산자 는

  • i의 평가 전에 i++(의 좌측면 +=)과
  • (의 오른쪽) i평가 전의 값 .i+++=

답변

먼저 i++0을 반환합니다. 그런 다음 i1 씩 증가합니다. 마지막으로 i초기 값에 i0을 더한 값과 i++0도 함께 설정됩니다. 0 + 0 = 0


답변

이것은 추상 구문 트리의 왼쪽에서 오른쪽으로 상향식 평가입니다. 개념적으로 식의 트리는 위에서 아래로 내려가지만 재귀가 맨 아래에서 트리로 다시 올라 오면서 평가가 전개됩니다.

// source code
i += i++;

// abstract syntax tree

     +=
    /  \
   i    ++ (post)
         \
         i

루트 노드를 고려하여 평가를 시작합니다 +=. 이것이 표현의 주요 구성 요소입니다. +=변수를 저장하는 위치를 결정하고 0 인 이전 값을 얻으려면 왼쪽 피연산자를 평가해야합니다. 다음으로 오른쪽을 평가해야합니다.

오른쪽은 증분 후 ++연산자입니다. 여기에는 하나의 피연산자 i가 있으며 값의 소스와 값을 저장할 위치로 평가됩니다. 연산자는를 i찾아서 찾은 다음 0결과적 1으로 해당 위치에 저장합니다 . 이전 값 0을 반환하는 의미에 따라 이전 값을 반환합니다.

이제 제어는 +=작업자 에게 돌아갑니다 . 이제 작업을 완료하는 데 필요한 모든 정보가 있습니다. 결과를 저장할 위치 (의 저장 위치 i)와 이전 값을 알고 있으며 이전 값에 추가 할 값, 즉을 알고 있습니다 0. 따라서 i0으로 끝납니다.

Java와 마찬가지로 C #은 평가 순서를 수정하여 C 언어의 매우 비현실적인 측면을 삭제했습니다. 왼쪽에서 오른쪽, 상향식 : 코더가 예상 할 수있는 가장 명확한 순서입니다.


답변

때문에 i++첫 번째 반환 값은, 다음을 증가시킵니다. 그러나 i가 1로 설정되면 다시 0으로 설정합니다.


답변

증가 후 방법은 다음과 같습니다.

int ++(ref int i)
{
    int c = i;
    i = i + 1;
    return c;
}

따라서 기본적으로을 호출 i++하면 i증분이지만 원래 값은 0으로 반환됩니다.