C/C++ : 가변 인수 매크로 함수 구현

va_args in c

예전에 가변 인수를 받는 C 함수를 만드는 법을 간단히 살펴본 적이 있는데, 오늘은 좀 더 자세히 알아보자.

C 언어에서 복수 인자를 받는 함수는 다음과 같이 선언한다.

반환형 함수명(고정인자[, 고정인자] ,....);

최소 1개 이상의 고정인자가 있고, 그 이후에 ...을 써서 추가적인 인자를 더 받는다는 것을 알려준다.

가변인자 파싱하기

가변인자를 받기위해서는 va_start(), va_arg(), va_end() 매크로 함수를 사용하면 된다. 각 가변 인자의 값을 구해내기 위해서는 가변인자의 갯수와 타입이 필요하다. 기본적인 아이디어는 함수 호출시, 넘겨지는 파라미터들은 메모리상의 연속된 위치에 나열되므로, 이를 통해서 개수와 각 인자의 크기(타입으로부터 알 수 있는)를 구해내는 것이다.

  1. va_list 타입의 변수를 하나 선언한다. 예를 들어 vargs라고 하자.
  2. va_start(vargs, 마지막 고정인자)를 추가한다.
  3. 가변 인자의 수 만큼 루프를 돌며 va_arg(vargs, 타입)을 사용해 각 인자의 값을 구한다.
  4. 처리가 끝나면 va_end(vargs)를 쓴다. 뭐 안해도 상관을 없을 것 같지만, 플랫폼에 따라서는 필요하기도 하단다.

다음 함수는 가변인자를 받아 그 인자들의 합을 구하는 함수이다. 고정인자로 가변인자의 개수를 넘겨주는 방식이다.

int sum_of_numbers(int count, ...)
{
    va_list vargs; 
    /* va_list 타입은 그냥 포인터(void *)이다. 
    int i=0, sum=0;
    va_start(vargs, count);  /* vargs 변수에 첫번째 가변인자의 포인터를 저장한다. */
    while(i<count) {
        int t = va_arg(vargs, int); /* vargs로부터 int의 크기만큼 바이트를 읽은 값을 내놓는다. 그리고 vargs는 다음 포인터를 가리킨다. */ 
        sum += t;
        i++;
    }
    va_end(vargs); // clear vargs
    return sum;
}

원리를 알고 있다면 다음과 같은 방식으로 처리해도 된다. 가변함수들은 모두 int 타입이므로….

int sum_of_numbers(int count, ...)
{
    int i=0, sum=0;
    while(i<count){
       sum += *(int *)(&count + i + 1);
       i++;
    }
    return sum;
}

va_start, va_end 함수는 사실 함수가 아니라 매크로이다. 다음과 같이 구현해보았다.

typedef void* s_va_list;
#define s_va_start(A,B) (A)=(&B + 1);
#define s_va_arg(A,B) *((B*)(A)); (A) += sizeof(B);
#define s_va_end(A) (A)=NULL;

int sum_of_numbers(int count, ...) {
    int i=0, sum=0;
    s_va_list vargs;
    s_va_start(vargs, count);
    while(i<count){
        sum += s_va_arg(vargs, int);
        i++;
    }
    s_va_end(vargs);
    return sum;
}

끗.