ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Macro가 뭘까?
    Programming🧑‍💻/Cpp 2025. 2. 12. 15:36

     

    매크로는 전처리기에게 "이 텍스트를 저 텍스트로 바꿔주세요"라고 지시하는 일종의 치환 규칙입니다.

    메크로 기본 개념 

    가장 단순한 형태의 메크로는 #define을 사용한 상수 정의입니다. 

    #define PI 3.14159
    #define MAX_STUDENTS 30
    
    int main() {
        double circle_area = PI * radius * radius;  // 컴파일 전에 PI가 3.14159로 치환됨
        int students[MAX_STUDENTS];  // MAX_STUDENTS는 30으로 치환됨
    }

     

    여기서 중요한 것은 메크로가 동작하는 시점입니다. 컴파일러가 코드를 컴파일하기 전에, 전처리기가 먼저 모든 메크로를 치환합니다.

    위 코드는 전처리 후에 다음과 같이 변합니다. 

    int main() {
        double circle_area = 3.14159 * radius * radius;
        int students[30];
    }

     

    메크로는 함수처럼 인자를 받을 수도 있습니다. 

    #define SQUARE(x) ((x) * (x))
    #define MAX(a,b) ((a) > (b) ? (a) : (b))
    
    int main() {
        int result = SQUARE(5);  // 25가 됨
        int bigger = MAX(10, 20);  // 20이 됨
    }

     

    메크로의 장단점 

    장점

    1. 컴파일 시간 최적화 : 메크로는 컴파일 전에 처리되므로, 함수 호출 오버헤드가 없습니다. 

     

    일반적인 함수 호출 과정은 다음과 같습니다.

    int add(int a, int b) {
        return a + b;
    }
    
    int main() {
        int result = add(5, 3);
    }

     

    이 코드가 실행될 때 다음과 같은 과정이 일어납니다 

    1. 현재 실행 지점의 주소를 스택에 저장
    2. 함수 인자들을 스택에 푸시
    3. 프로그램 카운터를 함수의 시작 주소로 변경
    4. 함수 코드 실행
    5. 반환값을 레지스터나 스택에 저장
    6. 원래 실행 지점으로 돌아감

    이 모든 과정을 "함수 호출 오버헤드"라고 합니다.

     

    위와 같은 함수를 메크로를 이용해 구현하면 다음과 같습니다.

    #define ADD(a,b) ((a) + (b))
    
    int main() {
        int result = ADD(5, 3);
    }

     

    해당 코드가 전처리과정을 거치면 다음과 같이 바뀝니다. 

    #define ADD(a,b) ((a) + (b))
    
    int main() {
        int result = ADD(5, 3);
    }

    여기서는

    1. 함수 호출이 없음
    2. 스택 조작이 없음
    3. 프로그램 카운터 변경이 없음
    4. 단순히 더하기 연산만 수행

    이렇게 메크로를 사용하면 컴파일 이전의 전처리단계에서 해당 부분이 자동으로 치환되기 때문에 컴파일 이후 프로그램 실행시 함수를 호출하면서 생기는 오버헤드를 제거할 수 있습니다. 

     

    2. 타입 독립성 : 메크로는 타입에 구애받지 않고 사용할 수 있습니다. 

    #define MAX(a,b) ((a) > (b) ? (a) : (b))
    int i_max = MAX(10, 20);     // int형에서 동작
    double d_max = MAX(3.14, 2.72);  // double형에서도 동작

     

    메크로의 경우 단순히 텍스트를 치환하는 것으로 취급되기 때문에 타입에 영향을 받지 않습니다. 

    단점

     

    • 디버깅의 어려움: 전처리된 코드를 디버깅하기가 어렵습니다.
    • 타입 체크 불가: 컴파일러의 타입 체크를 받지 않아 예상치 못한 오류가 발생할 수 있습니다.
    • 복잡한 로직 구현의 어려움: 복잡한 로직을 매크로로 구현하면 코드가 매우 읽기 어려워집니다.

     

     

    현대적인 대안 

    위와 같은 단점 때문에 cpp에서는 다음과 같은 현대적인 방법들을 추천합니다. 

     

    1. 상수 메크로 대신 const 사용

    // 매크로 대신
    const double PI = 3.14159;
    const int MAX_STUDENTS = 30;

     

    2. 함수형 매크로 대신 인라인 함수 사용

     

    • 컴파일러에게 "가능하다면 함수 호출 대신 코드를 직접 삽입해달라"고 요청
    • 매크로와 비슷한 성능 최적화 가능
    • 하지만 타입 안정성과 디버깅의 용이성은 유지

     

    // 매크로 대신
    template<typename T>
    inline T square(T x) { return x * x; }
    
    template<typename T>
    inline T max(T a, T b) { return a > b ? a : b; }

    댓글

Designed by Tistory.