C나 C++을 공부하다보면 #define에 대해 알게 될 것이다 또한 남에 코드를 봐도 #define를 사용하는 코드를 자주 마주치게 되는데 이번 글에선 이러한 #define에 대해 무엇이고 어떤 기능을 하는지 알아보려고 한다
define이란?
#define은 C와 C++ 프로그래밍 언어에서 사용되는 프리프로세서 지시어로 참고로 여기서 프리프로세서는 컴파일 과정에서 소스 코드를 먼저 처리하는 프로그램을 말한다 #define 지시어는 주로 두 가지 목적으로 사용되는데 이 둘이 바로 매크로 정의와 조건부 컴파일이다
매크로 정의 (Macro Definition)
#define을 사용하면 매크로를 정의할 수 있는데 매크로는 특정 문자열이나 코드 조각을 대체하는 식별자입로 코드 내에서 반복적으로 사용되는 값이나 표현식을 간결하게 표현하는 데 유용하다
글만 보면 잘 이해가 되지 않을 수 있으니 예제 코드로 살펴보면
1
2
3
4
5
6
7
8
9
#define PI 3.14159
#define SQUARE(x) ((x) * (x))
int main() {
double radius = 5.0;
double area = PI * SQUARE(radius);
std::cout << "Area: " << area << std::endl;
return 0;
}
위와 같이 PI는 상수 3.14159를 SQUARE(x)는 x의 제곱을 계산하는 매크로 함수로 만들 수 있다
코드를 보면 알겠지만 #define 사용하면 훨씬 더 간결하고 깔끔하게 코드를 작성할 수 있을 것이라고 예상할 수 있다
조건부 컴파일 (Conditional Compilation)
많이들 #define을 매크로 용도로만 사용하지만 ifdef, endif와 함께 사용하여 조건부 컴파일을 할 수도 있다 주로 이 기능은 디버깅 코드를 추가하거나 특정 환경에 맞는 코드를 작성해야 할 때 사용되는데
말로는 어려우니 코드로 직접확인 해보자
1
2
3
4
5
6
7
8
9
10
#define DEBUG
int main() {
int value = 5;
#ifdef DEBUG
std::cout << "Debug: value 값 = " << value << std::endl;
#endif
// 기타 코드
return 0;
}
위 코드에서 #ifdef DEBUG는 DEBUG가 정의되어 있을 때만 내부의 코드가 컴파일 되는 코드이다
주의사항 및 대안
이렇게 유용한 기능을 가진 #define에도 주의할 점이 있는데 크게 타입 체크 부재와 전역적인 영향 문제이다 자세히 살펴보자면
타입 체크 부재: #define은 단순 텍스트 치환을 수행하므로 타입 오류가 발생할 수 있다
전역적인 영향: 매크로는 스코프가 없어 전역적으로 적용되기에 이름 충돌이 발생할 수 있다
위와 같다
가능한 경우 #define 대신 const, enum, 또는 inline 함수와 같은 안전한 대안을 사용하는 것이 좋은데 이들은 타입 안정성을 제공하며 네임스페이스 내에서 관리될 수 있어
이들을 이용하면 #define이 가진 문제에서 벗어나 있기에 안전하고 관리하기 쉬운 코드를 작성할 수 있다
1
2
3
4
5
6
7
8
9
10
11
12
const double Pi = 3.14159;
inline double square(double x) {
return x * x;
}
int main() {
double radius = 5.0;
double area = Pi * square(radius);
std::cout << "Area: " << area << std::endl;
return 0;
}
위 코드처럼 const와 inline 함수를 사용하여 #define에서와 동일한 기능을 구현할 수 있다는 걸 알 수 있다