함수 호출규약
‘Calling convention‘ (콜링 컨벤션) 이라고 부르는 함수 호출규약은, __cdecl 와 __stdcall 그리고 __fastcall 이 있다. 이 셋은 다 매개변수의 전달에 관련돼있다.
지난시간 매개변수는 오른쪽 매개변수부터 왼쪽 매개변수 순으로 스택에 저장된다고 했었다. 그걸 메모리 그리는 방법에서 부터 배웠었다. 그럼, 선언된 매개변수는 누가 해제할까? 그것을 표시해주는 것이 __cdecl, __stdcall, __fastcall이다.
예시
영상의 예제를 보면, int Add (int x, int y)의 함수가 나온다. 이때, 스택의 순서는 오른쪽부터해서 int(4바이트)형 매개변수가 2개(*2)니까 8바이트가 되는데, 이것을 포함하고, 그 쌓인 스택을 정리하는 것이 Caller일까 Callee일까 아니면 레지스터일까 하는 것은 호출 규약에 따라 달라진다. 우선은 int Add (int x, int y)의 기본적인 함수를 가지고 해석해보자.
__cdecl, __stdcall, __fastcall
int Add (int x, int y)는 호출규약을 안 붙여준 상태로, 호출규약은 자료형과 함수의 이름 사이에 붙어서, int __cdecl Add (int x, int y)와 같다. 그럼 이 함수의 매개변수, 스택은 누가 정리할까? 바로 Caller다.
기본이 되는 호출규약, __cdecl에서는 함수를 부른 함수인 Caller(콜러)가 스택을 정리한다. 영상에서처럼 디버깅 모드로 풀어보면 기계어로 Caller가 정리하는 것을 확인할 수 있다.
그럼 __stdcall(스텐다드 콜)로 바꾸면 어떻게 변할까? 매개변수의 스택에 쌓이는 순서는 오른쪽에서 왼쪽으로 동일하다. 그러나 스택의 정리는 Callee가 한다. 매개변수를 포함하는 함수가 직접 정리도 하는 것이다. (둘의 차이는 거기서 거기다.)
마지막인 __fastcall(패스트 콜)은 이 둘과 다르게, 레지스터(Register)가 스택을 정리한다. 물론 스택의 정리는 오른쪽에서 왼쪽으로 Callee가 한다.
함수포인터와 호출규약
함수포인터의 기본형태로 “자료형 (* 함수이름 )(매개변수 자료형) = 함수;“와 같이 하고, 가리키는 함수는 __stdcall, __fastcall이라 하면, 에러가 난다. 왜냐하면 (* 함수이름)꼴로 하면, 기본함수인 __cdecl의 함수의 포인터가 되기 때문이다. 그러기에 함수의 호출규약이 붙은 함수는, 포인터도 마찬가지로 호출규약을 *앞에 붙여주어야 된다. 예를들어 int __stdcall Add (int x, int y)를 포인터로 가리키려면 int (__stdcall * pfAdd)(int,int) = Add;와 같이 해줘야 되는 것이다. 이점 명심하자.
'c언어 > 워딩(미정리)' 카테고리의 다른 글
자기참조 구조체 (0) | 2019.07.08 |
---|---|
구조체의 선언과 활용 (0) | 2019.07.08 |
버퍼 오버런 (0) | 2019.07.08 |
정적 라이브러리 개발 (0) | 2019.07.08 |
함수 포인터 (0) | 2019.07.08 |