관련글 : 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
========================================================================