C: gcc 에서 매크로를 함수처럼 쓰기

C 에서 매크로는 매우 강력한 기능을 제공한다. 하지만 몇 가지 아쉬운 점이 있는데, 매크로를 일반 함수처럼 쓰기에는 어려움이 있다는 것이다.

예를 들어, 어떤 정수 m 의 어떤 정수 n 제곱을 구하는 기능을 매크로로 구현하기란 쉽지 않다. 왜냐하면 거듭제곱의 결과값을 저장할 또 다른 변수가 필요하고, 반복문이 있어야 하며, 그 결과를 돌려주려면, 결국 복합문이 필요하기 때문이다. 물론 do{}while(0) 같은 기법을 써도 되지만, 아쉽게도 이것으로는 값을 되돌려줄 수가 없다.

gcc 는 이를 위해 statement expression 이라는 확장 기능을 제공한다. 위의 기능을 제공하는 매크로를 POW() 라고 하자. POW() 는 다음처럼 쓸 수 있다.

1
2
3
4
5
6
7
8
9
#define POW( m, n ) ({      \
    int POW_m = (m);        \
    int POW_n = (n);        \
                            \
    while( POW_n-- > 0 )    \
        POW_m *= POW_m;     \
                            \
    POW_m;                  \
})



마지막 8 번째 줄에 있는 POW_m 의 값이 POW() 의 결과값이다. 하지만 이 기능은 위에서도 말했듯이 확장 기능이므로, -pedantic 옵션을 사용하면 다음과 같은 경고가 발생한다.

warning: ISO C forbids braced-groups within expressions

이를 방지하기 위해서는 다음처럼 __extension__ 을 사용하면 된다.

1
2
3
4
5
6
7
8
9
#define POW( m, n ) __extension__ ({    \
    int POW_m = (m);                    \
    int POW_n = (n);                    \
                                        \
    while( POW_n-- > 0 )                \
        POW_m *= POW_m;                 \
                                        \
    POW_m;                              \
})



그런데 주의할 것은 이것 역시 매크로이기 때문에, 내부에서 쓰이는 변수들은 외부에서 쓰이는 변수와 겹치지 않아야 한다는 것이다. 2 번째, 3 번째 줄에서 POW_m 또는 POW_n 으로 선언한 이유가 그것이다.물론 외부에서 POW_m 또는 POW_n 이 쓰이고 있다면 문제가 될 수 있지만...

이 기능의 또다른 장점은 매크로의 매개변수를 오로지 한 번만 평가할 수 있다는 것이다. 예를 들어 MAX() 를 보자. MAX() 의 경우 일반적인 정의는 다음과 같다.

#define MAX(a, b) ((a) > (b) ? (a) : (b))

다음과 같은 코드를 생각해 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

#define MAX( a, b ) (( a ) > ( b ) ? ( a ) : ( b ))

int main( void )
{
    int a = 10;
    int b = 20;
    int c = MAX( a, b++ );

    printf("a = %d, b = %d, c= %d\n", a, b, c );

    return 0;
}


우리의 기대는

a = 10, b = 21, c = 20 

이다. 하지만 실제 결과는

a = 10, b = 22, c = 21

이다. 왜냐하면 MAX() 에서 매개변수가 여러번 평가되기 때문이다. MAX() 를 풀어보면 다음과 같다.

MAX( a, b++ ) = (( a ) > ( b++ ) ? ( a ) : ( b++ ))

보다시피, b++ 가 두번 평가되기 때문에 MAX() 를 사용하고 나면, b 가 1 이 증가되는 것이 아니라 2 가 증가된다. 그리고 MAX() 의 결과값은 b++ 의 영향으로 b 보다 1 이 큰 값이 된다.

이를 방지하려면 MAX() 를 다음과 같이 써야 한다.

1
2
3
4
5
6
#define MAX( a, b ) __extension__ ({    \
    int MAX_a = (a);                    \
    int MAX_b = (b);                    \
                                        \
    MAX_a > MAX_b ? MAX_a : MAX_b;      \
})


어떤 이는 차라리 static inline 함수를 쓰는 것이 낫지 않냐고 할 수 있다. 물론 C99 컴파일러라면 statinc inline 함수를 써도 상관이 없다. 하지만 매크로가 가지고 있는 특유의 기능, 예를 들어 # 이나 ## 같은 전처리기 명령이 필요하다면 또다른 선택지는 없을 것 같다.

Statement expression 에 대해 더 자세히 알고 싶으면 다음 링크를 참조하기 바란다.


아울러, gcc 의 모든 확장 기능에 대해 알고 싶다면 다음 링크를 참조하기 바란다.




댓글

이 블로그의 인기 게시물

토렌트: < 왕좌의 게임 > 시즌 1 ~ 시즌 8 완결편 마그넷

토렌트: NGC < 코스모스 > 우리말 더빙 전편(1편~13편) 마그넷

토렌트: < 스타워즈 > Ep.1 ~ Ep.6 마그넷