함수를 호출하게 되면 그 파라미터(인자)들을 전달해야 됩니다.

그러기 위해서 파라미터를 스택에 저장해서 전달하게 되는데

저장하는 순서는 항상 오른쪽에서 왼쪽으로 합니다.

그러니까 첫번째 파라미터가 항상 제일 마지막에 저장됩니다.

그렇게 함으로써 SP+6이 항상 첫번째 인자를 가르키도록 하는 것입니다. (어셈블리어 관점입니다.)

아무튼 차이점은 그 함수가 종료했을 때 스택에 저장된 파라미터를 누가 지우냐하는 문제가

남습니다. 가장 좋은 것은 함수가 지우는 것입니다.

그렇게 함으로써 좋은 점은 코드의 길이가 짧아진다는 것입니다.

(아니면 함수를 호출할 때마다 그 뒷부분에 스택에서 인자를 지우는 부분이 따라야 겠죠?)

이렇게 함수가 인자를 스택에서 지우는 방식을 stdcall이라고 합니다.

하지만 가변인자의 경우(예를 들어 sprintf처럼)에는 함수를 호출한 쪽에서 지우는 것이

안전합니다. 어떤 인자들을 저장했는지 호출한 쪽은 정확히 알지만 함수쪽에서 불분명합니다.

정확히 말하자면 사용자가 형변환등을 실수로 해서 함수가 다른 데이터형으로 인식하거나해서

잘못 지우면 어떤 결과가 날지 모르니까 호출한 쪽에서 지우니 편이 안전하다는 거죠.

정리를 하면 stdcall은 함수가 인자를 스택에서 지우는 형태

cdecl은 호출한 쪽에서 인자를 스택에서 지우는 형태로

stdcall은 일반적인 Windows API, CALLBACK 함수에 사용되고

cdecl은 Windows API 중 가변인자일 경우에 사용됩니다.

그리고 참고로 보통 C++에서 위의 것을 다 생략하면 thiscall이 사용되는데

(thiscall은 키워드가 아니니 사용하지 마세요.)

방법은 cdecl과 동일하고 차이점은 추가로 this 포인터가 제일 마지막에 스택에 저장된다는

것입니다.

//////////////////////////////////////////////////////////////////////////////////


함수의 호출 관례에 대해서 궁금해 하시는 분들이 많은것 같아
부족한 실력이지만 정리해서 올립니다..많은 도음이 됐음 하네요^^
우선,함수의 호출관례(function calling convention)란,
함수의 파라미터를 스택(stack)에 푸시(push)하는 순서,푸시하는쪽 및 이름 변화(name mangling)을 명시한 것입니다..

표로 정리 해보면..

변경자 푸시(push)순서 팝(pop)하는쪽 이름변화
_cdecl Right first Caller '_'prepended
_fastcall Left first Callee '@'prepended
_pascal Left first Callee Uppercase
_stdcall Right first Callee No change

Right first는 함수의 파라미터를 오른쪽에서 왼쪽으로 평가하여 스택에 푸시하는것을 나타냅니다.

Caller/Callee는 호출하는쪽 /호출 당하는쪽 에서 스택 동작을 하는것을 의미하구요..

이름 변화란.. 함수 이름 오버로딩..(이름이 같은 함수를 구분하기 위해서 컴파일러는 이름 장식(name mangling)작업을 하죠..) 에서 쓰이는데(어셈브러 소스를 보시면 알수 있습니다..)
c방식의 경우 명칭 앞에 언더스코어_가 붙는다는 애기죠.

Windows API(Application programning Interface)함수는 모두 WIN32방식(_stdcall)을 사용합니다..
이것은 파라미터를 팝하는 쪽이 Callee이기 때문이죠..(아마도 윈도우즈 운영체제에 이미 존재하는 함수들에 대해서 일관된 파라미터 팝 방식을 적용함으로써 얻어지는 최적화의 이점 때문인듯..)
만약 호출하는 쪽에서 파라미터를 팝해야 한다면 ,실행 코드를 생성할때 컴파일러는 이부분의 코드를 생성해야 되겠죠.. 컴파일러의 부담이 커지는것은 둘째치고(실행파일의 크기증가),운영체제의 관할 영역을 건드리는 보호차원에서 문제가 될수 있습니다..

모든 C++컴파일러에서 기본값은 cdecl 입니다..
콜백함수에 명시적으로 _stdcall 즉,WIN32혹은 WINAPI를 지정해야하는 이유죠...
(*참고)WIN32는 windows.h에 선언된 _stdcall의 매크로입니다.
WIN32에서 _pascal은 _stdcall로 대치되었습니다.

Windows응용프로그램의 시작함수인 WinMain()은 _stdcall이지만
WIN32콘솔 응용프로그램에서 시작함수인 main()은 여전히 _cdecl입니다.

호출관례에 대한 예를 들어보면..

#include <iostream.h>

void _pascal f (int i,int j) {
cout<< i << " " << j <<endl;
}
void _cdecl g(int i ,int j) {
cout<< i << " " << j <<endl;
}

void main() {
int i,j;

i=1; j=2;
f(i==j,i=j) ;//왼쪽에서 오른쪽으로 평가(left to right evaluation)
g(i==j,i=j);//오른쪽에서 왼쪽으로 평가(right to left evaluation)
}

실행 결과는 0,2
1,2

pascal방식은 파라미터 푸시를 왼쪽에서 오른쪽으로 한다는 사실을 주의해야겠죠..
f(i==j,i=j);에서 i==j가 먼저 평가되어 거짓이 되어서 "0"이 출력 되는것입니다..

windows에서 더이상 pascal방식은 존재 하지 않습니다._stdcall로 대치되었습니다.

Posted by 응이

Dream come true.
응이

달력

태그목록