선행 처리기란, 실제로 소스코드를 컴파일 하기 이전에, 선행 처리 명령문에 따라서 소스코드를 수정하는 과정을 하는 것을 의미한다.
선행 처리기는 #으로 선언된 명령문들을 단순히 치환하는 과정을 한다. 예를 들어 #define PI 3.14 라고 선언 했다고 아래의 예와 같이 가정하자.
#define PI 3.14
int main(){
int result = 10 * PI;
}
위의 소스코드는 선행 처리기를 거치면 아래와 같이 PI 부분이 3.14로 치환 된다.
int main(){
int result = 10 * 3.14;
}
이렇게 선행 처리기를 거치면, 명령문은 사라지고 명령문에 작성된 명령은 코드에 적용되어 치환된다.
#define으로 정의되는 선행 처리 명령문이다. #define 지사자 뒤에 오는 것을 매크로라고 하고, 이를 선행 처리기가 매크로로 정의된 상수를 대체한다.
#define MAGIC_NUMBER 1234
매크로 상수는 위와 같이 선언하고 선행 처리 과정 후 소스코드에 사용된 MAGIC_NUMBER 변수가 모두 1234로 대체된다.
매크로는 상수 뿐만 아니라 함수의 형태로도 선언 할 수 있다.
#define SQUARE(X) X*X
위와 같이 함수 형태로 매크로를 정의 할 수 있는데, 위의 함수를 예시로 설명 해보자면, SQUARE(X)의 형태를 X*X로 치환하라는 의미가 된다. 다시말해, SQUARE(4)의 형태로 사용한다면 해당 매크로는 4*4로 치환 된다는 것이다.
하지만 여기서 주의할 점이 있다. 매크로 함수를 선언하고 사용할때, 괄호의 유무가 매우 중요하다는 것이다. 선행 처리기는 실제로 매크로 함수를 함수로 취급하여 호출하게끔 하는것이 아니라, 단순히 정의된 매크로를 치환 하는것 뿐이다. 그렇기 때문에 괄호의 유무에 따라 계산 결과가 달라질 수 있다.
SQUARE(3+2) // 3+2*3+2 = 11
SQUARE((3+2)) // (3+2)*(3+2) = 25
위와 같이 괄호의 유무에 따라 치환된 후 계산 결과가 달라지게 된다.
보다 안전하게 매크로 함수를 정의 하기 위해서는 전달 인자를 각각 괄호로 다 감싸고, 전체를 괄호로 한번 더 감싸는것이 안전한 정의 방법이다.
#define SQUARE(X) ((X)*(X))
매크로 함수의 장점은 다음과 같다.
그리고 단점은 다음과 같다.
매크로 함수는 위와 같은 장단점이 존재하기 때문에 적절한 곳에 잘 사용해야 한다. 주로 사용이 빈번하고 간단하고 작은 크기의 함수를 매크로 함수로 정의하면 좋다.