함수의 호출 관례(function calling convention)란 함수의 파라미터를 스택에 푸시하는 순서, 푸시하는 쪽 및 이름 변화를 명시한 것이다.
변경자 |
푸시 순서 |
팝하는 쪽 |
이름 변화 |
__cdec |
오른쪽에서 왼쪽으로 |
호출하는 쪽 |
prepended |
__fastcall |
왼쪽에서 오른쪽으로 |
호출당하는 쪽 |
@ prepended |
__pascal |
왼쪽에서 오른쪽으로 |
호출당하는 쪽 |
Uppercase |
__stdcall |
오른쪽에서 왼쪽으로 |
호출당하는 쪽 |
No change |
푸시 순서
위쪽 표의 푸시 순서의 의미는 함수의 파라미터가 어느쪽에서부터 먼저 푸시를 하는지에 대한 말.
팝하느 쪽
팝하는 쪽의 의미는 함수 호출 후 종료 시점에서 함수의 스택을 정리할 때 스택의 팝을 어느쪽에서 하느냐에 대한 말이다.
이름 변화
- C방식의 경우 명칭 앞에 언더스코어 '_'가 붙는다.
- 레지스터 방식의 경우 @가 붙는다.
- Pascal 바익의 경우 모두 대문자로 바뀐다.
- win32 방식(__stdcall)의 경우 이름은 __stdcall이다.
windows의 api 함수는 모두 win32 방식을 이용한다.
모든 C++ 컴파일러에서 기본값은 cedecl이다.
visual studio IDE에서 calling convention을 바꿀 수 있다.
visual c++에서 Windows 프로그래밍을 하더라도 기본 설정은 __cdecl이다.
windows 응용 프로그램의 시작 함수인 WinMain()은 __stdcall이지만 win32 콘솔 응용 프로그램의 시작 함수인 main()은 __cdecl이다.
#include <iostream>
using namespace std;
void __pascal f(int i, int j) {
cout << i << " " << j << "\n";
}
void __cdecl g(int i, int j) {
cout << i << " " << j << "\n";
}
int main() {
int i, j;
i = 1; j = 2;
f(i == j, i = j);
i = 1; j = 2;
g(i == j, i = j);
return 0;
}
__pascal일 경우 왼쪽에서 오른쪽으로 스택에 푸쉬되므로 "0 1"로 될 것이고
__cdecl일 경우 오른쪽에서 왼쪽으로 스택에 푸쉬되므로 "1 2"로 될 것이다.
visual studio ide로 이용 할 경우 컴파일 에러가 발생한다.
windows에서 더 이상 pascal 방식은 존재하지 않는다. 이것은 __stdcall로 대처되었다.
__fastcall로 바꿨다.
#include <iostream>
using namespace std;
void __fastcall f(int i, int j) {
cout << i << " " << j << "\n";
}
void __cdecl g(int i, int j) {
cout << i << " " << j << "\n";
}
int main() {
int i, j;
i = 1; j = 2;
f(i == j, i = j);
i = 1; j = 2;
g(i == j, i = j);
return 0;
}
이 상태에서 실행을 하면
__fastcall은 "0 2"이 나와야되고
__cdecl은 "1 2"이 나와야 된다.
근데 막상 실행해보면 둘 다 "1 2"나 "0 2"가 나올 수 있다. 왜그럴까..