김종하 파수닷컴 선임
의도하지 않은 결과(unexpected result)
매크로, 특히 함수처럼 사용하는 매크로(매크로 함수) 사용 시 인자를 주의해서 넣어야 합니다.
다음 매크로를 사용한다고 가정합니다.
#define DOUBLE(a) ((a) + (a))
이 매크로는 인자값 a를 두 번 더합니다. 2를 넣으면 4가, 5를 넣으면 10이 나오겠지요. 하지만 아래와 같이 인자를 넣으면 어떨까요?
//test1.c
#include <stdio.h>
#define DOUBLE(a) ((a) + (a))
int main()
{
int a = 2;
int b = DOUBLE(++a);
printf("b is %dn", b);
return 0;
}
예상 값은 a에 1을 더하니(++a) 값은 3이고, 이를 두 번 더하니 6이 나오겠지요?
하지만 실제 값은
gcc -o test1 test1.c
./test1
b is 8
8이 나옵니다.(사실 전 7을 예상했다는...) 왜 그럴까요? 컴파일러가 전처리를 거친 파일을 한 번 보면 그 답이 나옵니다.
gcc -E test1.c -o test1.i
int main()
{
int a = 2;
int b = ((++a) + (++a));
printf("b is %dn", b);
return 0;
}
전처리를 거치면 매크로 부분을 실제 코드로 치환하는데, 여기서 ++a 가 두 번 들어갑니다. 컴파일러 내부적으로 트리 형태로 위 표현식을 가지고 있는데요, + 보다 ++a 두 번을 먼저 수행합니다. 그럼 x값이 4가 되죠. 이 x 값을 두 번 더하니 8이 됩니다.
보통 함수형 매크로의 인자에 Side-effect가 있는 수식을 넣으면 의도하지 않은 결과가 나올 수 있습니다.
그렇다면 매크로 인자만 주의하면 되나요? 아니죠. 매크로 정의도 잘 해야 합니다. 다음 코드를 보겠습니다. 매크로 SUM을 주의해서 봐주세요.
//test2.c
#include <stdio.h>
#define MULTI(a, b) a * b
int main()
{
int a = 2;
int b = 10;
int c = MULTI(a + 2, b + 1)
printf("c is %dn", c);
return 0;
}
a에 2를 더한 값(4)와 b에 1을 더한 값(11)을 곱했으니 44가 나와야겠죠?
gcc -o test2 test2.c
./test2.c
c is 23
이상합니다. 전처리한 파일을 한 번 보겠습니다.
int main()
{
int a = 2;
int b = 10;
int c = a + 2 * b + 1;
printf("c is %dn", c);
return 0;
}
c를 초기화하는 수식을 보면 저희가 예상했던 값이 안 나오는 이유를 알 수 있습니다.
a + 2 * b + 1 은 연산자 우선순위에 의거, 2 * b 가 먼저 계산되고(결과 : 20) 여기에 a와 1을 더하기 때문에 23이 나오는 것이지요. 이런 문제를 막기 위해 함수형 매크로 정의 시, 아래와 같이 매크로 인자마다 괄호를 습관적으로 적어주면 문제의 소지를 없앨 수 있습니다.
#define MULTI(a, b) (a) * (b)
보통 이런 버그는 찾기도 어렵고, 컴파일러마다 그 결과 값이 다를 수 있기 때문에 고치기가 매우 까다롭습니다. 애당초 매크로를 사용하지 않거나, 잘 정의해서 이러한 버그 발생 가능성 자체를 봉쇄하는 것이 가장 좋은 방법입니다.
해당 글은 파수닷컴에서 아이티데일리 게재용으로 제공한 콘텐츠입니다.
저작권은 파수닷컴 측에 있으며, 무단전제 및 복제를 금합니다.
원문출처: http://blog.fasoo.com/220052815606