관련글 : http://tibyte.kr/252


1.

전위/후위 증감 연산자를 복합적으로 썼을 경우에 대한 분석을 간단하게 해 보았다.

C언어로 코드를 작성하여 VC++컴파일러로 x86어셈블리를 뽑았다.



2. 사례

1) 전후위 연산자를 앞뒤로 쓴 경우


 int a=1;

00E53427  mov         dword ptr [ebp-8],1 

int b=1;

00E5342E  mov         dword ptr [ebp-14h],1 

int c=1;

00E53435  mov         dword ptr [ebp-20h],1 

int num1 = b+++a+++c;

00E5343C  mov         eax,dword ptr [ebp-14h] 

00E5343F  add         eax,dword ptr [ebp-8] 

00E53442  add         eax,dword ptr [ebp-20h] 

00E53445  mov         dword ptr [ebp-2Ch],eax 

00E53448  mov         ecx,dword ptr [ebp-8] 

00E5344B  add         ecx,1 

00E5344E  mov         dword ptr [ebp-8],ecx 

00E53451  mov         edx,dword ptr [ebp-14h] 

00E53454  add         edx,1 

00E53457  mov         dword ptr [ebp-14h],edx 


→ num1 = b+++a+++c;에 대한 어셈블리 코드를 보면 처음 4행은 num1에 대한 연산을 하는 부분이다. 

ebp-14h, ebp-8, ebp-20h 메모리의 수를 그냥 더해서 2bp-2Ch(num1 변수)메모리에 복사한다.

num1의 결과값은 1+1+1 = 3이 된다.

그리고 그 다음줄(00E53448)부터 3행에서 ebp-8 메모리(변수 a)에 1을 더하고,

마지막 3행에선 ebp-14 메모리(변수b)에  1을 더해 두고 있다.

이처럼 앞뒤로 ++a++ 꼴이 될 경우엔 왼쪽 부터 처리된다는 것을 볼 수 있다.(당연한 일이겠지만...)






2) 같은 변수에 대해 전후위 증감 연산을 여러 번 쓴 경우


int a=5,b=5,c=5,num1,num2,num3;

00E5339E  mov         dword ptr [ebp-8],5 

00E533A5  mov         dword ptr [ebp-14h],5 

00E533AC  mov         dword ptr [ebp-20h],5 

num1 = (++a)+(++a);

00E533B3  mov         eax,dword ptr [ebp-8] 

00E533B6  add         eax,1 

00E533B9  mov         dword ptr [ebp-8],eax 

00E533BC  mov         ecx,dword ptr [ebp-8] 

00E533BF  add         ecx,1 

00E533C2  mov         dword ptr [ebp-8],ecx 

00E533C5  mov         edx,dword ptr [ebp-8] 

00E533C8  add         edx,dword ptr [ebp-8] 

00E533CB  mov         dword ptr [ebp-2Ch],edx 

→ 식에서 ++a 부분을 먼저 처리한다. 이 때, 한 번 증가 연산을 하고 나서 

  그 값을 다시 ebp-8 메모리에 복사하고 그 값을 다시 레지스터로 읽어와

  다음 1을 더하는 작동을 하므로 a변수는 총 2 증가하게 된다.

  여기까지가 ++a를 두 번 연산한 단계이다.

  그리고 결과값인 num1변수(ebp-2Ch 메모리)의 값으로는 a를 두 번 더한 7+7 = 14가 나온다.


num2 = (b++)+(b++);

00E533CE  mov         eax,dword ptr [ebp-14h] 

00E533D1  add         eax,dword ptr [ebp-14h] 

00E533D4  mov         dword ptr [ebp-38h],eax 

00E533D7  mov         ecx,dword ptr [ebp-14h] 

00E533DA  add         ecx,1 

00E533DD  mov         dword ptr [ebp-14h],ecx 

00E533E0  mov         edx,dword ptr [ebp-14h] 

00E533E3  add         edx,1 

00E533E6  mov         dword ptr [ebp-14h],edx 

→ 앞의 항과 뒤의 항을 먼저 더하고 ebp-38h메모리(num2변수)에 복사 후

ebp-14h메모리(b변수)는 ecx레지스터와 edx레지스터를 거쳐 증가가 두 번 된다.

결과값은 num은 5+5=10 이고 b는 7이다.


num3 = (++c)+(c++);

00E533E9  mov         eax,dword ptr [ebp-20h] 

00E533EC  add         eax,1 

00E533EF  mov         dword ptr [ebp-20h],eax 

00E533F2  mov         ecx,dword ptr [ebp-20h] 

00E533F5  add         ecx,dword ptr [ebp-20h] 

00E533F8  mov         dword ptr [ebp-44h],ecx 

00E533FB  mov         edx,dword ptr [ebp-20h] 

00E533FE  add         edx,1 

00E53401  mov         dword ptr [ebp-20h],edx

→  처음 3행을 보면 c값은 1 증가하여 6이 된다.

   그 다음 3행에서 c+c를 하여 num3(ebp-44h 메모리)에는 12가 들어간다. 

  마지막 3행에서 c변수에(ebp-20h 메모리) 1을 더한다.


위 세 가지 사례들을 종합해 보면, 식의 전위 증감 연산자들이 먼저 처리되어

그 결과(전위증감연산)를 반영한 값으로 식이 계산된 후에, 후위 증감 연산자가 처리된다는 것을 알 수 있다.





3) 함수의 인자에 썼을 때.


function(n++,++n);

00B517E5  mov         eax,dword ptr [ebp-8] 

00B517E8  add         eax,1 

00B517EB  mov         dword ptr [ebp-8],eax 

00B517EE  mov         ecx,dword ptr [ebp-8] 

00B517F1  mov         dword ptr [ebp-0d0h],ecx 

00B517F7  mov         edx,dword ptr [ebp-8] 

00B517FA  add         edx,1 

00B517FD  mov         dword ptr [ebp-8],edx 

00B51800  mov         eax,dword ptr [ebp-8] 

00B51803  push        eax  

00B51804  mov         ecx,dword ptr [ebp-0d0h

00B5180A  push        ecx  

00B5180B  call         00B511C2 

00B51810  add         esp,8 


↓ 이 코드가 동작할 때 두 개의 메모리와 eax ecx edx 레지스터의 상태를 표로 만들어 보았다. 

   (글을 작성하다 n을 선언하는 부분을 빼먹었는데, n의 초기값은 10 였다고 하겠습니다.)


00B517E5  mov         eax,dword ptr [ebp-8] 

00B517E8  add         eax,1 

00B517EB  mov         dword ptr [ebp-8],eax 

→ 함수호출시 두 개의 인수 중 두 번째가 스택에 나중에 들어가므로 먼저 처리된다(후입선출)

  즉 function(n++, ++n)에서 뒤쪽의 ++n이 먼저 처리되는 것이다.

 n변수(ebp-8 메모리)에 1을 더해 둔다. 

그러면 이 시점에서 n의 값은 11.



00B517EE  mov         ecx,dword ptr [ebp-8] 

00B517F1  mov         dword ptr [ebp-0d0h],ecx 

00B517F7  mov         edx,dword ptr [ebp-8] 

00B517FA  add         edx,1 

00B517FD  mov         dword ptr [ebp-8],edx 

→ n의 값을 ebp-0d0h 메모리에 복사한다.

  그리고 n에 1을 더한다. 이 시점에서 n은 12.



00B51800  mov         eax,dword ptr [ebp-8] 

00B51803  push        eax  

00B51804  mov         ecx,dword ptr [ebp-0d0h

00B5180A  push        ecx  

→ ebp-8 메모리의 값을 eax레지스터로 복사해 와서 함수 스택에 push 한다.

  그리고 ebp-0d0h 메모리의 값도 함수 스택에 push.

  먼저 push 되는게 마지막 인자이다.

  그래서함수 내부에서 매개변수의 값을 찍어보면

  첫 번째 인자의 값은 11,

  두 번째 인자의 값은 12이다.



00B5180B  call         00B511C2 

00B51810  add         esp,8 

→ 다음에 실행할 인스트럭션이 있는 주소 00B511C2로 이동한다.
  함수가 4바이트 매개변수 2개를 썼으므로 esp레지스터에 4*2=8을 더한다.



3. 결론
전,후위 증감 연산자를 헷갈리지 않게 의미가 잘 드러나도록 쓰자.. 

내용추가+)
3가지 컴파일러로 컴파일해 본 결과 : http://tibyte.kr/252



========================================================================





+ Recent posts