김종하 파수닷컴 선임

▲ 김종하 파수닷컴 선임 [PA사업팀 EA팀] http://story.wisedog.net/
[아이티데일리] 1편에서는 C/C++ 언어에서 매크로 사용 시, 타입 검사를 하지 않아서 위험하다는 글을 올렸습니다. 2편에서도 C/C++ 언어 매크로 사용 시 위험성에 대해서 적어보도록 하겠습니다.

의도하지 않은 결과(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

저작권자 © 아이티데일리 무단전재 및 재배포 금지