순수 C언어 함수는 일반적으로 함수 파라메터의 갯수가 고정적이고 OOP에서 보이는 오버로딩과 같이 하나의 함수이름으로 서로 다른 파라메터를 가지는 함수를 사용할 수 없다. 그런데 C언어 함수 중 printf()류의 함수는 파라메터가 최소 1 개 이상으로 가변적이다. 예를 들면 아래 printf 함수는 동일한 결과를 출력하지만 사용한 파라메터의 갯수는 다르다.
printf("Hello World!"); /* 파라메터 1개 사용 */
printf("Hello %s!", "World"); /* 파라메터 2개 사용 */
printf("%s %s!", "Hello", "World"); /* 파라메터 3개 사용 */
printf("Hello %s!", "World"); /* 파라메터 2개 사용 */
printf("%s %s!", "Hello", "World"); /* 파라메터 3개 사용 */
그러면 어떻게 이런 함수를 작성할 수 있을까? 해결책은 헤더파일 "stdarg.h"에 정의돼 있다. 이 파일에 보면 va_list 타입과 네 가지의 매크로 va_start, va_arg, va_end, va_copy 가 정의돼 있는데, 이 매크로들이 가변 파라메터를 처리하는데 사용된다.
va_list
이 타입은 가변 파라메터를 위한 데이터 타입으로 가변 함수 정의 부분에서 가변 파라메터를 처리하기 위해 선언한다.
void va_start(va_list ap, last);
이 매크로는 가변 파라메터(ap)의 시작위치를 함수에 주어진 마지막 고정 파라메터(last) 다음으로 초기화 해 주며, 일반적으로 함수의 첫 부분에 사용된다.
type va_arg(va_list ap, type);
이 매크로는 가변 파라메터(ap) 에서 주어진 타입(type)의 값을 읽고 가변 파라메터(ap)의 위치를 다음으로 이동시킨다.
void va_end(va_list ap);
이 매크로는 함수의 마지막 부분에 사용한다.
void va_copy(va_list dest, va_list src);
이 매크로는 src에 저장된 가변 파라메터를 dest로 복사한다.
이 타입은 가변 파라메터를 위한 데이터 타입으로 가변 함수 정의 부분에서 가변 파라메터를 처리하기 위해 선언한다.
void va_start(va_list ap, last);
이 매크로는 가변 파라메터(ap)의 시작위치를 함수에 주어진 마지막 고정 파라메터(last) 다음으로 초기화 해 주며, 일반적으로 함수의 첫 부분에 사용된다.
type va_arg(va_list ap, type);
이 매크로는 가변 파라메터(ap) 에서 주어진 타입(type)의 값을 읽고 가변 파라메터(ap)의 위치를 다음으로 이동시킨다.
void va_end(va_list ap);
이 매크로는 함수의 마지막 부분에 사용한다.
void va_copy(va_list dest, va_list src);
이 매크로는 src에 저장된 가변 파라메터를 dest로 복사한다.
그리고 가변 함수를 정의할 때 최소 한 개 이상의 파라메터는 반드시 정상적으로 "int a"와 같이 타입과 변수명을 선언해야 하며 나머지 가변 파라메터 부분은 "..."으로 선언하면 된다. 이 때 "..."은 함수 파라메터의 맨 마지막에 선언해야 한다.
사용법은 아래 프로그램을 참조하자. 아래 작성한 프로그램의 함수 sum은 가변 파라메터를 가지는 함수로, 주어진 파라메터의 값이 -1을 만날 때까지 파라메터에 전달된 정수의 합을 계산한다.
#include <stdio.h>
#include <stdarg.h>
int sum_list(int a, ...)
{
va_list ap; /* 가변 파라메터 저장을 위한 변수 ap 선언 */
int i, sum = 0;
va_start(ap, a); /* 가변 파라메터가 a 다음부터 시작됨을 알림 */
i = a;
while(i != -1) {
sum += i;
i = va_arg(ap, int); /* 가변 파라메터에서 정수 값을 읽고 다음 값을 읽기 위해 준비 */
}
va_end(ap); /* 가변 파라메터를 다 사용함 */
return sum;
}
int main(int argc, char *argv[])
{
printf("%d\n", sum_list(-1)); /* 0 출력 */
printf("%d\n", sum_list(1,-1)); /* 1출력 */
printf("%d\n", sum_list(1,2,-1)); /* 3 출력 */
printf("%d\n", sum_list(1,2,3,-1)); /* 6출력 */
printf("%d\n", sum_list(1,2,3,4,-1)); /* 10 출력 */
printf("%d\n", sum_list(1,2,3,4,5,-1)); /* 15 출력 */
printf("%d\n", sum_list(1,2,3,4,5,6,-1)); /* 21 출력 */
printf("%d\n", sum_list(1,2,3,4,5,6,7,-1)); /* 28 출력 */
printf("%d\n", sum_list(1,2,3,4,5,6,7,8,-1)); /* 36 출력 */
printf("%d\n", sum_list(1,2,3,4,5,6,7,8,9,-1)); /* 45 출력 */
printf("%d\n", sum_list(1,2,3,4,5,6,7,8,9,10,-1)); /* 55 출력 */
return 0;
}
#include <stdarg.h>
int sum_list(int a, ...)
{
va_list ap; /* 가변 파라메터 저장을 위한 변수 ap 선언 */
int i, sum = 0;
va_start(ap, a); /* 가변 파라메터가 a 다음부터 시작됨을 알림 */
i = a;
while(i != -1) {
sum += i;
i = va_arg(ap, int); /* 가변 파라메터에서 정수 값을 읽고 다음 값을 읽기 위해 준비 */
}
va_end(ap); /* 가변 파라메터를 다 사용함 */
return sum;
}
int main(int argc, char *argv[])
{
printf("%d\n", sum_list(-1)); /* 0 출력 */
printf("%d\n", sum_list(1,-1)); /* 1출력 */
printf("%d\n", sum_list(1,2,-1)); /* 3 출력 */
printf("%d\n", sum_list(1,2,3,-1)); /* 6출력 */
printf("%d\n", sum_list(1,2,3,4,-1)); /* 10 출력 */
printf("%d\n", sum_list(1,2,3,4,5,-1)); /* 15 출력 */
printf("%d\n", sum_list(1,2,3,4,5,6,-1)); /* 21 출력 */
printf("%d\n", sum_list(1,2,3,4,5,6,7,-1)); /* 28 출력 */
printf("%d\n", sum_list(1,2,3,4,5,6,7,8,-1)); /* 36 출력 */
printf("%d\n", sum_list(1,2,3,4,5,6,7,8,9,-1)); /* 45 출력 */
printf("%d\n", sum_list(1,2,3,4,5,6,7,8,9,10,-1)); /* 55 출력 */
return 0;
}
다음은 "FreeBSD Library Functions Manual Man Page"에 제시된 또다른 예제 프로그램이다. 이 예제의 va_arg 매크로를 보면 가변 파라메터의 타입이 서로 다른 것도 처리할 수 있음을 알 수 있다.
void foo(char *fmt, ...)
{
va_list ap;
int d;
char c, *s;
va_start(ap, fmt);
while (*fmt)
switch(*fmt++) {
case 's': /* string */
s = va_arg(ap, char *);
printf("string %s\n", s);
break;
case 'd': /* int */
d = va_arg(ap, int);
printf("int %d\n", d);
break;
case 'c': /* char */
/* Note: char is promoted to int. */
c = va_arg(ap, int);
printf("char %c\n", c);
break;
}
va_end(ap);
}
{
va_list ap;
int d;
char c, *s;
va_start(ap, fmt);
while (*fmt)
switch(*fmt++) {
case 's': /* string */
s = va_arg(ap, char *);
printf("string %s\n", s);
break;
case 'd': /* int */
d = va_arg(ap, int);
printf("int %d\n", d);
break;
case 'c': /* char */
/* Note: char is promoted to int. */
c = va_arg(ap, int);
printf("char %c\n", c);
break;
}
va_end(ap);
}