va_args in c
예전에 가변 인수를 받는 C 함수를 만드는 법을 간단히 살펴본 적이 있는데, 오늘은 좀 더 자세히 알아보자.
C 언어에서 복수 인자를 받는 함수는 다음과 같이 선언한다.
반환형 함수명(고정인자[, 고정인자] ,....);
최소 1개 이상의 고정인자가 있고, 그 이후에 ...
을 써서 추가적인 인자를 더 받는다는 것을 알려준다.
가변인자 파싱하기
가변인자를 받기위해서는 va_start()
, va_arg()
, va_end()
매크로 함수를 사용하면 된다. 각 가변 인자의 값을 구해내기 위해서는 가변인자의 갯수와 타입이 필요하다. 기본적인 아이디어는 함수 호출시, 넘겨지는 파라미터들은 메모리상의 연속된 위치에 나열되므로, 이를 통해서 개수와 각 인자의 크기(타입으로부터 알 수 있는)를 구해내는 것이다.
va_list
타입의 변수를 하나 선언한다. 예를 들어 vargs라고 하자.va_start(vargs, 마지막 고정인자)
를 추가한다.- 가변 인자의 수 만큼 루프를 돌며
va_arg(vargs, 타입)
을 사용해 각 인자의 값을 구한다. - 처리가 끝나면
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;
}
끗.