매크로와 주의점
매크로란?
C언어와 C++에서 매크로란 #define 전처리 구문(선행처리 지시문)으로 전처리기가 컴파일 전에 정해진 구문을 정해진 문장으로 치환해주는 기능을 말합니다.
(여기서 ‘정해진 구문’이란 매크로의 이름을, ‘정해진 문장’이란 그 이름에 대응하는 코드를 의미합니다.)
말이 조금 어려우니, 간단히 설명하자면, 자주 사용되는 값을 편하게 쓸 수 있게 만들어주는 기능입니다.
이때, 인자가 없는 매크로를 매크로 변수라고 합니다.
1
2
3
4
5
6
7
#define PI 3.141592 //앞으로 PI 만으로 3.141592라는 값을 대신 쓸 수 있음.
int main(void)
{
float degree = 90.0f;
float radian = degree * PI / 180.0f; //PI를 직접 입력하지 않고도 사용 가능
}
매크로는 인자를 넘겨서 긴 문장에 인자를 넣어 치환해줄 수도 있는데, 이를 매크로 함수라고 합니다.
여기에서 ‘긴 문장’이라고 표현한 것은 매크로 함수는 함수 형태의 연산 뿐만이 아니라 특정 함수 호출의 형태를 줄이는 등, 다양한 것을 줄일 수 있기 때문입니다.
1
2
3
4
5
6
7
8
9
#define ADD(X, Y) (X + Y) //연산을 가독성 좋게 함수처럼 쓸 수 있음
#define LOG(X) printf("간단한 출력 결과는: %d\n", X)
int main(void)
{
int result = ADD(1, 2); //ADD 매크로 함수를 통해 계산
LOG(result); //LOG 매크로 함수를 통해 결과 출력
return 0;
}
결국 매크로란,
매크로라는 단어의 의미 그대로 정해진 규칙이나 패턴을 편하게 쓸 수 있도록 만들어주는 기능입니다.
매크로는 컴퓨터 과학 분야에서 정해진 순서에 따라 어떻게 특정한 입력 시퀀스 가 출력 시퀀스 로 매핑되어야 하는지를 정의하는 규칙이나 패턴을 말한다.
출처: 매크로 (컴퓨터 과학) - 위키백과, 우리 모두의 백과사전 (wikipedia.org)
매크로 사용시 주의점
매크로는 프로그래머의 프로그램 작성을 편하게 해주는 대신 여러 문제점이 있습니다.
1. 매크로 변수는 타입을 정할 수 없다.
매크로는 단순 치환만 해주기 때문에 타입을 지정해줄 수 없습니다.
1
#define PI 3.141592 //앞으로 PI 만으로 3.141592라는 값을 대신 쓸 수 있음.
이 예제에서 PI가 float인지, double인지 심지어는 int인지 알 수 없습니다.
물론, 이는 개발자의 의도일 수도 있고 해당 타입을 명확히 알고 있어서 올바르게 사용 할 수도 있습니다. 다만, 실수할 여지가 존재합니다.
2. 매크로 함수는 코드 치환 방식이기 때문에 동작에 실수가 발생할 수 있다.
매크로 함수 역시 1번의 문제와 비슷하게 단순 치환만 하기 때문에 동작에 실수가 발생할 수 있습니다.
1
#define ADD(X, Y) (X + Y) //연산을 가독성 좋게 함수처럼 쓸 수 있음
이 예제에서 만약 (X + Y)
가 아니라 X + Y
였다면? 즉, 괄호가 없었다면 연산자 우선순위에 의한 문제가 발생할 수 있습니다.
1
int result = ADD(1, 2) / 2;
위와 같은 연산을 수행했을 때, 우리가 원하는 결과는 1.5를 원했던 것이라면, 매크로를 사용했을 때에는 1 + 2 / 2
와 같이 연산되기 때문에 2가 나오게 될 것입니다.
3. 매크로는 디버깅이 어렵다.
매크로는 컴파일 되기 전에 사전에 정의된 코드로 치환되는 것이기 때문에, 그 자체로는 완전한 코드라고 보기 어렵습니다. 이로 인해 매크로를 디버깅하는 것이 어렵습니다.
더 간단히 풀어 설명하자면,
함수는 실행 중에 중단점을 설정하여 디버거에서 코드의 흐름을 멈추고 상태를 확인할 수 있지만, 매크로는 이런 방식으로 중단점을 설정할 수 없습니다.
왜냐하면 매크로는 컴파일 이전에 이미 코드에 치환되어 있기 때문입니다. 따라서 매크로를 디버깅하는 것은 그 실행 과정을 디버거를 통해 단계별로 확인하기가 어렵습니다.
결론
결국 매크로라는 것은 코드를 좀 더 편하게 해주는 도구이지만,
타입을 체크하지 않는다는 점, 여러 주의점과 문제점에 의해 사용할 때 신중해야 합니다.
이러한 불편한 점들 때문에 C++에서는 상수(const)와 인라인 함수(inline)를 사용하는 것이 좋습니다.
특히, C++11 이후로 constexpr
이라는 키워드가 도입되면서 매크로 변수의 대용으로 많이 사용하고 있습니다.
매크로 대신 const를 사용하면 얻는 이점:
- 데이터형을 명시적으로 지정할 수 있습니다.
- 매크로는 전처리기에 의해 단순히 문자열 치환으로 처리되므로, 데이터형의 정보가 사라집니다.
반면에const
나constexpr
는 변수이므로 데이터형을 가지고 있습니다.
- 매크로는 전처리기에 의해 단순히 문자열 치환으로 처리되므로, 데이터형의 정보가 사라집니다.
- 스코프 규칙에 의해 그 정의를 제한할 수 있습니다.
- 매크로는 파일 전체에서, 또는
#include
된 모든 파일에서 사용할 수 있습니다.
이는 예상치 못한 이름 충돌을 일으킬 수 있습니다.
그러나const
나constexpr
는 일반 변수와 같은 스코프 규칙을 따릅니다.
따라서 특정 함수나 클래스, 네임스페이스에서만 사용할 수 있도록 제한할 수 있습니다.
- 매크로는 파일 전체에서, 또는
- 배열이나 구조체 같은 보다 복잡한 데이터형에도 const 를 사용할 수 있습니다.
- 매크로는 단순한 상수나 문자열 치환에 주로 사용되지만,
const
나constexpr
는 모든 데이터형에 사용할 수 있습니다.
예를 들어,const
배열,const
구조체,const
클래스 객체 등을 선언할 수 있습니다.
- 매크로는 단순한 상수나 문자열 치환에 주로 사용되지만,
따라서, 가능한 경우에는 const
나 constexpr
를 사용하고 함수의 경우에는 inline
을 사용하는 것이 보다 안전하고 효율적인 코드를 작성하는 데 도움이 될 수 있습니다.