본문 바로가기
개발/C++ (98,03,11,14,17,20,23)

Before Classic C++ : Preprocessor

by snowoods 2025. 10. 30.

Before Classic C++

 

전처리기 (Preprocessor)

 

개요

전처리기(preprocessor)는 소스 코드가 컴파일러에 의해 컴파일되기 전에 먼저 처리되는 특별한 지시문입니다. 주로 파일 포함, 매크로 정의, 조건부 컴파일 등의 기능을 수행합니다.

 

C++ 버전별 주요 키워드 도입 시기

  • C99 이전 (C/C++): #include, #define, #if, #endif 등 대부분의 전처리기 지시문은 C 언어 초기부터 존재했으며, C++에서도 처음부터 지원되었습니다.
  • C++11/14/17: const, constexpr, inline 함수, 템플릿 등 타입에 안전하고 성능이 뛰어난 기능들이 강화되면서, 전통적인 #define 매크로의 사용은 점차 줄어드는 추세입니다.

 

내용 설명

#include

  • #include <파일명>: 컴파일러가 지정한 표준 라이브러리 경로에서 헤더 파일을 찾아 포함합니다.
  • #include "파일명": 현재 소스 파일이 있는 디렉토리에서 먼저 파일을 찾고, 없으면 표준 라이브러리 경로에서 찾아 포함합니다. 주로 사용자가 작성한 헤더 파일을 포함할 때 사용합니다.

#define

  • 매크로 상수: 특정 값을 상징하는 이름으로 대체합니다. #define 이름 값 형태로 사용합니다.
  • 매크로 함수: 코드를 인라인으로 치환하는 함수 형태의 매크로입니다. 복잡한 매크로는 디버깅이 어렵고 예기치 않은 부작용을 낳을 수 있어 주의해야 합니다.

 

예제 코드

#include <iostream> // 표준 라이브러리 포함

// 매크로 상수 정의
#define PI 3.14159
#define MESSAGE "Hello, World!"

// 매크로 함수 정의
// 매개변수는 항상 괄호로 감싸는 것이 안전합니다.
#define SQUARE(x) ((x) * (x))

int main() {
    std::cout << "--- Macro Constant ---" << std::endl;
    std::cout << MESSAGE << std::endl;
    double radius = 5.0;
    double area = PI * radius * radius;
    std::cout << "Area of circle with radius " << radius << " is " << area << std::endl;

    std::cout << "\n--- Macro Function ---" << std::endl;
    int num = 7;
    std::cout << "Square of " << num << " is " << SQUARE(num) << std::endl;

    // 매크로의 위험성 예시
    int val = 3;
    // SQUARE(val + 1) -> ((val + 1) * (val + 1)) -> ((3 + 1) * (3 + 1)) = 16 (올바른 동작)
    // 만약 #define SQUARE(x) x * x 였다면 -> 3 + 1 * 3 + 1 = 7 (잘못된 결과)
    std::cout << "Square of " << val << "+1 is " << SQUARE(val + 1) << std::endl;

    return 0;
}

 

실행 결과

--- Macro Constant ---
Hello, World!
Area of circle with radius 5 is 78.5397

--- Macro Function ---
Square of 7 is 49
Square of 3+1 is 16

 

활용팁

  • 상수: #define 대신 constconstexpr를 사용하면 타입 검사가 가능하고 디버깅이 용이하여 더 안전합니다.
  • 함수: 간단한 함수는 #define 매크로 대신 inline 함수나 템플릿 함수를 사용하는 것이 타입 안전성 및 예측 가능성 측면에서 훨씬 좋습니다.
  • 조건부 컴파일: #ifdef, #ifndef, #if 등의 지시문을 사용하면 특정 조건에 따라 코드 블록을 컴파일에 포함하거나 제외할 수 있어, 플랫폼별 코드를 작성하거나 디버그 빌드를 관리할 때 유용합니다.