F.R.I.D.A.Y.

매크로 함수를 사용할 때 주의할 점 본문

DEV/C C++

매크로 함수를 사용할 때 주의할 점

F.R.I.D.A.Y. 2019. 11. 26. 23:21
반응형

 조금 복잡한 계산들. 즉, 복잡한 수식들을 반복해서 작성하기는 여간 귀찮은 것이 아닙니다. 그래서 우리는 전처리 명령어(define)를 이용해서 매크로 함수로 그 수식을 대신하곤 합니다. 이번 포스트에서는 이 매크로 함수를 작성할 때 무엇을 주의해야 하는지 알아봅니다.


매크로 함수

 일단 아래 코드를 예시로 들겠습니다. 간단히 두 수를 더하는 함수를 만들었다고 합시다.

#include <stdio.h>

int add(int a, int b){
	return a +b;
}

int main(void){
	
	int a,b;
	scanf("%d %d", &a, &b);
	
	printf("%d + %d = %d\n", a, b, add(a,b));
	
	return 0;
}

 우리 블로거는 add 함수를 이용하는 것은 프로그램 속도에 영향을 주기 때문에 다른 방법을 이용하기로 합니다. 바로 매크로 함수입니다.

#include <stdio.h>

#define add(a,b) a + b

int main(void){
	
	int a,b;
	scanf("%d %d", &a, &b);
	
	printf("%d + %d = %d\n", a, b, add(a,b));
	
	return 0;
}

 이제 이 프로그램을 실행해보면 정상적으로 두 입력값의 합산 결과가 나오게 됩니다.

정상적으로 계산됨

 그런데 이 블로거가 조금 더 계산하기로 했습니다. 입력받은 두 값의 연산 결과에 10을 곱하기로 말이죠.

#include <stdio.h>

#define add(a,b) a + b

int main(void){
	
	int a,b;
	scanf("%d %d", &a, &b);
	
	printf("%d + %d = %d\n", a, b, add(a,b) * 10);
	
	return 0;
}

 이 코드는 함수에서는 문제가 없습니다. 그러나 함수가 아닌 매크로 함수, 특히나 위 코드와 같이 작성된 매크로 함수의 경우에는 문제가 발생합니다. 이것은 전처리 지시자의 특성에 의한 것으로 특성에 대한 내용은 다음 링크를 참고하세요. 핵심은 아래 문장입니다.

https://pang2h.tistory.com/203

 전처리 지시자는 치환의 성격을 띠다 보니, 위 코드는 결국 아래 코드로 변경되어 컴파일이 진행됩니다.

#include <stdio.h>

int main(void){
	
	int a,b;
	scanf("%d %d", &a, &b);
	
	printf("%d + %d = %d\n", a, b, a + b * 10);
	
	return 0;
}

 사칙연산(프로그래밍)의 우선순위는 [곱셈 = 나눗셈 > 뺄셈 = 덧셈]입니다. 우리 의도는 a와 b의 값을 더한 후, 그 결과에 10을 곱하는 것인데 위 코드는 명백히 b와 10을 곱한 값을 a에 더하는 식이 되어버립니다. 전처리 지시자가 치환의 특성을 띤다는 점을 꼭 기억해야 하는 이유입니다.

 그렇다면 이 문제를 어떻게 해결해야 할까요? 사용하지 않기에는 매크로 함수는 너무 편리합니다.


괄호 이용

 이 문제는 결국 연산자 우선순위로 발생하는 문제였습니다. 따라서 우리는 매크로 함수 내부를 연산자 우선순위로 조정해주면 됩니다. 괄호를 이용합니다.

#include <stdio.h>

#define add(a,b) (a + b)

int main(void){
	
	int a,b;
	scanf("%d %d", &a, &b);
	
	printf("%d + %d = %d\n", a, b, add(a,b) * 10);
	
	return 0;
}

 add 매크로 함수에 괄호 하나 더 넣었을 뿐인데 이제 의도한 대로 값이 출력됩니다. 어린 개발자가 자주 하기 쉬운 실수입니다. 위 사례뿐만 아니라 아래와 같은 문제들도 있습니다.

#include <stdio.h>

#define mul(a,b) (a * b)

int main(void){
	
	int a,b;
	scanf("%d %d", &a, &b);
	
	printf("%d * %d = %d\n", a, b, mul(a,b));
	
	return 0;
}

 add 매크로 함수를 곱하는 mul 함수로 변경했습니다. 앞서 괄호를 이용하라고 했던 조언을 받아 괄호도 작성했습니다. 그러나 이 코드도 문제가 발생할 수 있는 코드입니다.

	printf("%d * %d = %d\n", a, b, mul(a+1,b+1));

 만일 위와 같은 코드를 작성하면 어떻게 될까요? 예시로 a에는 4, b에는 10을 넣어보겠습니다.

 15가 출력됩니다. 왜 이런 문제가 발생할까요? 앞서 언급드린 것과 마찬가지로 전처리 지시자의 특성인 치환 때문입니다. 위 코드를 치환하면 아래와 같은 코드로 변경됩니다.

	printf("%d * %d = %d\n", a, b, (a+1 * b+1));

 풀어 보면 결국 a와 b, 그리고 1을 더하는 것으로 바뀌어 버립니다. 이러한 경험을 바탕으로 우리는 매크로 함수로 들어오는 인자에도 괄호를 작성해주어야 한다는 결론에 도달하게 됩니다.

#define add(x, y) ((x) + (y))
#define mul(x, y) ((x) * (y))

 등과 같은 코드처럼 말이죠.

728x90
반응형
Comments