본문 바로가기

카테고리 없음

함수 호출 규약 in C++

C++에서 함수 호출 규약(Calling Convention)은 함수가 호출될 때 인수를 어떻게 전달하고, 반환값을 어떻게 처리하며, 스택을 누가 정리하는지를 정의하는 방식입니다. 주요 호출 규약은 다음과 같습니다.


cdecl (C Declaration)

  • 기본적인 C/C++ 함수 호출 규약
  • 인수 전달: 스택 (오른쪽에서 왼쪽)
  • 반환값: EAX (32비트), RAX (64비트)
  • 스택 정리: 호출자(caller)
  • 가변 인자 지원: 가능 (printf 같은 함수에서 사용)
  • MSVC에서 _cdecl, GCC에서 __attribute__((cdecl))로 지정
int __cdecl add(int a, int b) {
    return a + b;
}

stdcall (Standard Call)

  • Windows API에서 많이 사용
  • 인수 전달: 스택 (오른쪽에서 왼쪽)
  • 반환값: EAX / RAX
  • 스택 정리: 피호출자(callee)
  • 가변 인자 지원: 불가능
  • MSVC에서 _stdcall, GCC에서 __attribute__((stdcall))로 지정
int __stdcall multiply(int a, int b) {
    return a * b;
}

fastcall

  • 일부 인수를 레지스터를 사용하여 전달 (MSVC: ECX, EDX)
  • 나머지는 스택을 사용 (오른쪽에서 왼쪽)
  • 반환값: EAX / RAX
  • 스택 정리: 피호출자(callee)
  • 가변 인자 지원: 불가능
  • MSVC에서 _fastcall, GCC에서 __attribute__((fastcall))로 지정
int __fastcall subtract(int a, int b) {
    return a - b;
}

thiscall

  • C++ 멤버 함수에서 사용되는 기본 호출 규약
  • this 포인터를 ECX 레지스터(32비트)로 전달
  • 나머지 인수는 스택을 사용 (오른쪽에서 왼쪽)
  • 반환값: EAX / RAX
  • 스택 정리: 피호출자(callee)
  • 직접 지정 불가 (컴파일러가 자동 적용)
class MyClass {
public:
    int add(int a, int b);  // thiscall 사용 (컴파일러에 의해 자동 적용)
};

vectorcall (MSVC 전용)

  • 벡터 타입(예: __m128)을 레지스터로 전달하는 호출 규약
  • MSVC에서 _vectorcall로 지정
  • 가변 인자 지원: 불가능
int __vectorcall processVector(float x, float y, float z) {
    return x + y + z;
}

SysV ABI (System V AMD64 ABI)

  • 리눅스 및 macOS 64비트 시스템에서 사용되는 기본 호출 규약
  • 인수 전달:
    • 정수형: RDI, RSI, RDX, RCX, R8, R9
    • 부동소수점: XMM0 ~ XMM7
  • 나머지는 스택을 사용 (오른쪽에서 왼쪽)
  • 반환값: RAX / XMM0
  • 스택 정리: 호출자(caller)

명시적 호출 규약 적용

함수 포인터에 호출 규약을 지정할 수도 있습니다.

typedef int (__stdcall *FuncPtr)(int, int);

이렇게 하면, 해당 함수 포인터가 stdcall 규약을 따르는 함수만 가리킬 수 있습니다.


요약

호출 규약 인수 전달 방식 스택 정리 가변 인자 지원 주 사용처
cdecl 스택 (R → L) 호출자 O 기본 C/C++
stdcall 스택 (R → L) 피호출자 X Windows API
fastcall 레지스터(ECX, EDX) + 스택 피호출자 X 최적화
thiscall this는 ECX + 스택 피호출자 X C++ 멤버 함수
vectorcall 벡터 레지스터 사용 피호출자 X SIMD 최적화
SysV ABI 레지스터 (RDI, RSI, ...) + 스택 호출자 O 리눅스 64비트

Q&A

Q: 함수 호출 규약을 선택하는 기준은 무엇인가요?

A: 일반적으로 cdecl이 기본이며, Windows API에서는 stdcall을 사용합니다. 최적화가 필요하면 fastcall이나 vectorcall을 고려할 수 있습니다.

Q: 가변 인자를 지원하는 호출 규약은 무엇인가요?

A: cdecl과 SysV ABI는 가변 인자를 지원하지만, stdcall, fastcall, thiscall, vectorcall은 지원하지 않습니다.

Q: 서로 다른 호출 규약을 혼용하면 어떤 문제가 발생하나요?

A: 함수 호출 규약이 다르면 스택 정리 방식이 다르므로 크래시 또는 스택 오염이 발생할 수 있습니다. 따라서 함수 호출 규약을 일관되게 사용해야 합니다.

Q: 64비트 환경에서는 어떤 호출 규약을 사용하나요?

A: Windows의 64비트에서는 fastcall 기반의 Microsoft x64 calling convention을 사용하며, Linux/macOS에서는 SysV ABI가 기본입니다.


결론

C++에서 호출 규약을 잘 이해하면, 함수 호출 최적화다른 언어와의 인터페이스(예: Windows API, DLL 호출)에서 유용하게 활용할 수 있습니다. 특히, 플랫폼에 따라 기본 호출 규약이 다를 수 있으므로 주의해야 합니다. 🚀