-
__cdecl, __stdcall 차이점Programming Language/C++ 2017. 1. 2. 00:06
(2014년에 네이버 블로그에서 작성했던 글을 티스토리로 옮기며 이관했습니다)
1. __cdecl
함수 호출용으로 스택에 쌓은 파라미터를 호출한 함수에서 제거해 주는 Convention이다.
알기 쉽게 간단한 예시를 보자.
1234567891011121314#include <stdio.h>int __cdecl cdeclFunction(int a, int b){return a + b;}int main(){int varA = 1, varB = 2;cdeclFunction(1, 2);return 0;}위 C 코드를 Assembly로 짜보았다.
(어셈블리 프로그래밍이 익숙하지 않아서 문법에 맞지 않는 부분이 있을 수 있는데 중요하지 않으니 넘어가도록 하자)
123456789101112131415161718192021222324252627// cdeclFunction 함수 //push ebpmov ebp, espmov eax, [ebp+0x8]add eax, [ebp+0xC]mov esp, ebppop ebpret// main 함수//push ebpmov ebp, espsub esp, 0x8mov [ebp-0x4], 1 // varAmov [ebp-0x8], 2 // varBmov eax, [ebp-0x8]push eaxmov eax, [ebp-0x4]push eaxcall cdeclFunctionadd esp, 8 // 사용하지 않을 파라미터들이므로 값을 덮어쓸 수 있도록 해 준다.xor eax, eaxmov esp, ebppop ebpret22 라인에서 cdeclFunction을 call 한 뒤, 23 라인에서 8바이트만큼의 스택을 재사용 할 수 있도록 스택 포인터에서 add 해 준다.
main 함수(호출시킨 함수)에서 이런 정리를 해 주는 것을 cdecl 호출 방식이라고 한다.
예전부터 cdecl이 무슨 약자인지 알고 싶었는데 정리하면서 찾아보니 C DECLaration라는 뜻이란다.
declaration이 한국어로 선언이라는 뜻이라는데 C DECLaration를 직역하자면 -> C 선언이라는 뜻이 된다.
주관적인 생각이지만 이렇게 옛날에 만들어진 것들은 네이밍 센스가 정말 극악인 것 같다.
저런 예약어나 함수 이름들 중 문서를 보지 않고서는 무슨 역할을 하는지 알기 어려운 것들이 정말 많다.
2. __stdcall
cdecl 호출 방식과는 다르게 호출 당한(?) 함수 내부에서 stack pointer 연산을 해 준다.
위의 간단한 코드를 stdcall 호출 방식의 C/Assembly로 다시 짜보면 아래와 같다.
1234567891011121314#include <stdio.h>int __stdcall cdeclFunction(int a, int b){return a + b;}int main(){int varA = 1, varB = 2;cdeclFunction(1, 2);return 0;}12345678910111213141516171819202122232425// cdeclFunction 함수 //push ebpmov ebp, espmov eax, [ebp+0x8]add eax, [ebp+0xC]mov esp, ebppop ebpret 8// main 함수//push ebpmov ebp, espsub esp, 0x8mov [ebp-0x4], 1 // varAmov [ebp-0x8], 2 // varBmov eax, [ebp-0x8]push eaxmov eax, [ebp-0x4]push eaxcall cdeclFunctionxor eax, eaxmov esp, ebppop ebpret위의 cdecl 호출 방식과 비교해 보면 main 함수에서 cdeclFunction을 call하고, esp 레지스터에 연산해 주는 과정이 빠졌고,
cdeclFunction 함수에서 리턴할 때 8바이트를 pop함으로써 esp 레지스터가 기리키는 주소가 함수 호출 준비 전(파라미터 push 전)으로 복구된다.
이렇게 호출된 함수 내부에서 esp 레지스터를 복구시켜 주는 호출 형식을 stdcall이라고 한다.
* Win32 API가 보통 stdcall 형식으로 호출된다고 한다.
(https://msdn.microsoft.com/ko-kr/library/zxk0tw93.aspx)
3. 차이점
사실 stack pointer를 어디서 연산하느냐의 차이지 저걸 왜 나누어 놓았는지 이해가 되지 않았다.
stdcall 호출 방식이 cdecl 호출 방식보다 빠르고, 프로그램 용량도 적어진다고 하는데 그래도 모르겠다.
stdcall 방식이 빠르면 cdecl를 버리고 stdcall만 쓰면 될 것을 왜 쓸데없이 나누었는지, 저게 무슨 차이길래 속도 차이가 나는건지 궁금했다.
그래서 둘의 차이점을 알아보았다.
(참고 : http://blog.naver.com/kimchulgoon/30024494772)
일단 stdcall 호출 형식은 호출한 함수 내부에서 파라미터 크기만큼 pop을 해 주기 때문에 가변인자를 사용하는 것이 불가능하다고 한다.
그리고 속도, 용량의 이점은 8086 CPU를 설계할 때 리턴 후 어차피 stack pointer 위치를 변경할 건데
굳이 호출 후 연산을 해야 하나 싶어서 ret N 과 같은 명령어를 만든 것이라고 한다.
예전부터 궁금했지만 알아보기 귀찮아서 넘어갔던 것들인데 이렇게 간단한 이유일 줄은 몰랐다.
'Programming Language > C++' 카테고리의 다른 글
인라인 함수 (0) 2017.08.08 [Win32 API] 윈도우 모니터 중간으로 위치 이동하기 (0) 2017.08.08 [Win32 API] 다이얼로그 아이콘 변경 (0) 2017.08.08 LRESULT / CALLBACK / WPARAM / LPARAM (0) 2017.07.09 C++ 템플릿 클래스는 언제 생성될까? (0) 2016.12.10