블로그 이미지
대갈장군

calendar

1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31

Notice

2009. 7. 28. 01:43 프로그래밍/C
호출 규약은 함수를 호출하는 방식에 대한 약속을 정의한 것인데 예를 들자면 호출하는 쪽에서 메모리를 정리하는지, 인수는 어떻게 전달 할 것인지, 그리고 리턴 값은 어떻게 반환할 것인지 등을 정의 할 수 있다.

함수를 호출할 때는 스택(Stack)을 사용한다. 알다시피 스택은 메모리 영역의 아래부분에 위치하여 LIFO (Last in First out)의 원칙대로 동작하며 항상성을 유지한다. 

함수가 호출될 때 스택에는 함수로 전달되는 인수, 실행을 마치고 복귀할 주소, 그리고 지역 변수의 정보등이 저장되는데 이것을 스택 프레임 (Stack Frame)이라고 한다. 

이제 C에서 사용하는 각종 호출 규약을 살펴보자. 

총 다섯 가지가 있다. __cdecl, __stdcall, __fastcall, thiscall, naked 이다.

아마도 Visual Stuido를 사용해본 사람이라면 __cdecl과 __stdcall 그리고 thiscall을 자주 봤을 것이다. (에러 메세지에서)

__fastcall과 naked는 C / C++ 에서는 거의 사용하지 않기 때문에 설명이 필요없다. 원래 __fastcall은 레지스터 공간을 이용해 인수를 전달함으로써 좀더 빠른 속력을 내고자 했으나 범용성이 떨어져서 사용이 힘들고 naked는 C나 C++이 아닌 다른 언어로 작성된 함수 호출시 호출 규약을 정하지 않고 사용할 때 붙이는 접미사인데 이 역시도 나에게는 거의 사용 가능성이 0%다.

그러면 남은 것이 3가지인데 이중에서 thiscall은 멤버 함수이면 자동으로 붙게되는 호출 규약이다. 이 호출 규약이 붙으면 자동으로 객체의 포인터가 인수로 전달된다는 점이 특별하다. 

이 놈은 사용자가 임의로 가져다 쓸수 있는 선언자가 아니고 컴파일러가 알아서 붙여주는 '호출규약'일 뿐이므로 이 마저도 제외해야 한다. 결국 남는것은 딱 두개. __cdecl과 __stdcall이다.

이 두놈의 큰 차이점 두가지는,

1. 스택을 정리하는 주체가 다르다는 점이다. __cdecl은 호출한 쪽에서 정리하고 __stdcall은 호출된 함수측 에서 스택을 정리한다.

2. 가변 인수 함수라면 무조건 __cdecl을 사용해야 한다. 왜냐면 __stdcall은 함수측에서 스택을 정리해야 하는데 가변인수이므로 인수가 몇개나 들어올지 미리 알 길이 없다. 그래서 가변 인수 함수의 경우는 무조건 __cdecl을 사용해야 한다. 

호출 규약이 불일치 하는 경우에 발생하는 문제점은 치명적이다. 헌데 이런 오류가 발생하기는 상당히 힘든데 C로 작성해서 C로 호출하는 경우 호출 규약이 불일치 할 경우는 거의 없기 때문이다.

아무튼 어찌 어찌 해서 호출 규약이 서로 불일치하게 되면 호출한 쪽에서는 함수쪽에서 스택을 해제할 것이라 굳게 믿고, 반대로 함수측에서는 호출한 쪽에서 스택을 해제할 것이라 굳게 믿게되는 이상한 상황이 발생한다.

그렇게 되면 항상성을 잃게 되고 메모리에 오류가 발생하여 99.99992% 확률로 다운될 것이다. 다운이 안되더라도 엉망징찬이 될 게 뻔하다. 

아무튼, VS 2005를 사용하여 프로그램을 작성하다 보면 에러 메세지에 __cdecl과 __stdcall등 다양한 호출 규약의 접미어가 붙어 있는 것을 보게 되는데 이것을 보고 당황할 필요가 없다는 것이다. :)
posted by 대갈장군