호출 규약

Jaemyeong Lee·2024년 6월 7일

C/C++

목록 보기
51/56

GPT

C/C++에서 호출 규약(Calling Convention)에 대해 설명하고, 이를 이해하기 위한 다양한 방식을 제공하겠습니다.

1. 호출 규약 개요

호출 규약은 함수 호출 시 함수 인수를 전달하고, 반환 값을 처리하며, 스택을 정리하는 규칙을 정의합니다. 호출 규약은 함수 호출 시의 일관성을 유지하고, 호출자와 피호출자 간의 인터페이스를 표준화하는 데 사용됩니다. 다양한 호출 규약이 있으며, 주요 규약으로는 cdecl, stdcall, fastcall 등이 있습니다.

2. 주요 호출 규약

cdecl (C Declaration)

  • 기본 C/C++ 호출 규약
  • 인수는 오른쪽에서 왼쪽으로 스택에 푸시됨
  • 호출자가 스택을 정리함
  • 가변 인수를 허용함
#include <iostream>

extern "C" int __cdecl add(int a, int b) {
    return a + b;
}

int main() {
    int result = add(5, 3);
    std::cout << "Result (cdecl): " << result << std::endl;
    return 0;
}

stdcall (Standard Call)

  • Windows API에서 주로 사용되는 호출 규약
  • 인수는 오른쪽에서 왼쪽으로 스택에 푸시됨
  • 피호출자가 스택을 정리함
#include <iostream>

extern "C" int __stdcall multiply(int a, int b) {
    return a * b;
}

int main() {
    int result = multiply(5, 3);
    std::cout << "Result (stdcall): " << result << std::endl;
    return 0;
}

fastcall

  • 인수의 일부를 레지스터를 통해 전달하여 호출 오버헤드를 줄임
  • 나머지 인수는 스택을 통해 전달됨
  • 피호출자가 스택을 정리함
#include <iostream>

extern "C" int __fastcall subtract(int a, int b) {
    return a - b;
}

int main() {
    int result = subtract(5, 3);
    std::cout << "Result (fastcall): " << result << std::endl;
    return 0;
}

3. 컴퓨터 구조 및 운영체제 측면에서의 이해

스택 프레임과 호출 규약

호출 규약은 함수 호출 시 스택 프레임을 어떻게 구성하는지를 정의합니다. 각 호출 규약은 인수를 스택에 푸시하는 순서와 스택을 정리하는 방법을 다르게 규정합니다. 이는 호출자와 피호출자 간의 일관성을 유지하고, 호출의 안전성을 보장합니다.

스택 메모리 구조:
+------------------+
|      ...         |
+------------------+
| 반환 주소        |
+------------------+
| 인수 n           |
+------------------+
| 인수 n-1         |
+------------------+
| ...              |
+------------------+
| 인수 1           |
+------------------+
| 인수 0           |
+------------------+
| 이전 프레임 포인터|
+------------------+
| 지역 변수        |
+------------------+
|      ...         |
+------------------+

레지스터 사용

일부 호출 규약에서는 인수를 레지스터를 통해 전달하여 호출 오버헤드를 줄입니다. 이는 함수 호출 시 스택 메모리 접근을 줄이고, 호출 속도를 높이는 데 기여합니다.

4. 그림과 삽화

호출 규약의 개념과 처리 과정을 이해하기 쉽게 그림과 표로 표현하였습니다.

cdecl 호출 규약:
main 함수 호출 -> add 함수 호출

스택 프레임:
+------------------+
| 반환 주소        | <-- 반환  스택 정리 (호출자)
+------------------+
| 인수 b           | 3
+------------------+
| 인수 a           | 5
+------------------+
| 이전 프레임 포인터|
+------------------+
|      ...         |
+------------------+

stdcall 호출 규약:
main 함수 호출 -> multiply 함수 호출

스택 프레임:
+------------------+
| 반환 주소        | <-- 반환  스택 정리 (피호출자)
+------------------+
| 인수 b           | 3
+------------------+
| 인수 a           | 5
+------------------+
| 이전 프레임 포인터|
+------------------+
|      ...         |
+------------------+

fastcall 호출 규약:
main 함수 호출 -> subtract 함수 호출

레지스터 및 스택:
+------------------+
| 반환 주소        | <-- 반환 후 스택 정리 (피호출자)
+------------------+
| 인수 b (레지스터 전달)  | 3
+------------------+
| 인수 a (레지스터 전달)  | 5
+------------------+
| 이전 프레임 포인터|
+------------------+
|      ...         |
+------------------+

5. 예제 코드 확장

다양한 상황에서 호출 규약을 사용하는 예제를 추가로 제공합니다.

호출 규약 간의 호환성

#include <iostream>

extern "C" int __cdecl add(int a, int b) {
    return a + b;
}

extern "C" int __stdcall multiply(int a, int b) {
    return a * b;
}

extern "C" int __fastcall subtract(int a, int b) {
    return a - b;
}

int main() {
    int result1 = add(5, 3);
    std::cout << "Result (cdecl): " << result1 << std::endl;

    int result2 = multiply(5, 3);
    std::cout << "Result (stdcall): " << result2 << std::endl;

    int result3 = subtract(5, 3);
    std::cout << "Result (fastcall): " << result3 << std::endl;

    return 0;
}

요약

호출 규약은 함수 호출 시 인수를 전달하고 반환 값을 처리하며, 스택을 정리하는 규칙을 정의합니다. 주요 호출 규약으로는 cdecl, stdcall, fastcall 등이 있으며, 각 규약은 인수를 전달하는 방법과 스택을 정리하는 방법이 다릅니다. 호출 규약은 함수 호출의 일관성을 유지하고, 호출자와 피호출자 간의 인터페이스를 표준화하여 호출의 안전성을 보장합니다. 운영체제는 이러한 호출 규약을 기반으로 함수 호출과 관련된 메모리 관리를 수행하여 프로그램이 효율적으로 실행될 수 있도록 돕습니다.

이와 같은 내용을 통해 C/C++의 호출 규약에 대해 더 깊이 이해할 수 있을 것입니다. 추가로 궁금한 사항이 있으면 알려주세요!

profile
李家네_공부방

0개의 댓글