__cdecl,__stdcall__fastcall 호출규약

__cdecl

C언어 표준 호출 규약이다. 파라미터는 오른쪽에서 왼쪽으로 스택을 통해 전달되며, 호출한 곳에서 스택을 정리한다. 특징적인건 호출한 쪽에서 스택을 정리하기 때문에 가변 인자를 지원한다는 것이다. 

  1. extern "C" int __cdecl CdeclFunc(int a, int b, int c)  
  2. {  
  3.     printf("%d %d %d\n", a, b, c);  
  4. }  
위 함수를 호출하기 위한 어셈블리 코드는 아래와 같다. add 명령어를 통해서 호출 한 후에 스택을 정리해 주어야 한다.

 

push 3
push 2
push 1
call CdeclFunc
add esp, 12

 

 

__stdcall

윈도우 API의 표준 호출 규약이다. 파라미터는 오른쪽에서 왼쪽으로 스택을 통해서 전달되며, 스택 정리는 호출 당한 곳에서 이루어진다.

  1. extern "C" int __stdcall StdcallFunc(int a, int b, int c)  
  2. {  
  3.     printf("%d %d %d\n", a, b, c);  
  4. }  

 호출 코드는 아래와 같다. __cdecl과 유사하지만 스택 정리 코드가 없는게 특징이다.

 

push 3
push 2
push 1
call _StdcallFunc@12

 

 

 

__fastcall

말 그대로 빠른 호출이다. 파라미터 중 일부를 레지스터를 통해서 전달하는 함수다. x86 계열에서는 일반적으로 ecx, edx로 파라미터를 전달하고 나머지는 스택으로 전달한다. __cdecl과 같이 오른쪽에서 왼쪽으로 파라미터를 전달하며, 스택 정리는 호출을 당한 곳에서 수행한다. 따라서 가변 인자를 지원하지 못한다. 

  1. extern "C" int __fastcall FastcallFunc(int a, int b, int c)  
  2. {  
  3.     printf("%d %d %d\n", a, b, c);  
  4. }  

 위 함수를 호출 하는 코드는 아래와 같다. 앞 쪽 두 개의 파라미터가 ecx와 edx를 통해서 전달된다. 파라미터가 두 개 이하인 함수에 대해서는 스택을 전혀 사용하지 않기 때문에 빠르게 동작한다.

push 3
mov edx, 2
mov ecx, 1
call @FastcallFunc@12
 
< 스택 호출에 따른 이유 >
__cdecl 을 제외한 다른 호출들은 호출 당한 곳에서 함수 종료 시점에서 파라미터들이 들어있는 스택을 정리하는데..
이유는 속도에 있다. 
x86 CPU의 경우 ret 어셈블리 명령어를 두 가지 형태로 지원한다. 
ret를 하면 단순히 스택에 저장된 복귀 주소로 리턴 한다. 하지만 ret imm16을 사용하면 imm16만큼 스택에서 팝 한 다음 
꺼내진 복귀 주소로 리턴 한다. 위에서 살펴 보았듯이 __cdecl은 스택 정리를 위해서 add 명령어가 추가된다. 반면에 호출을 
당한 곳에서 정리하는 방식은 리턴할 때에 ret imm16을 사용함으로써 add 명령어가 추가되지 않아도 된다. 또한 486을 
기준으로 했을 때 ret와 ret imm16은 5클럭 사이클을 소모하는 동일한 명령어로 처리된다. 
따라서 __cdecl보다는 다른 호출 규약이 근소하게 빠를 수 있다. 
< 사용된 소스 >
  1. int _tmain(int argc, _TCHAR* argv[])  
  2. {  
  3.     CdeclFunc(1, 2, 3);  
  4.     FastcallFunc(1, 2, 3);  
  5.     StdcallFunc(1, 2, 3);  
  6.  
  7.     CCallConv conv;  
  8.     conv.ThisCall(1,2,3);  
  9.     return 0;  


< 출처 : http://www.jiniya.net/lecture/techbox/callconv.html >
위로 스크롤