가변인자란 인자의 개수가 변하는 인자를 말한다. 이 가변인자는 타입과 개수가 정해지지 않는다.
특정 함수를 사용할 때 이러한 가변인자를 사용하고 싶은 경우가 있다.(ex. printf
에서 여러개의 인자를 받아 출력)
이를 위해 C 언어에서는 stdarg.h
에 포함된 va_list
타입, va_arg
, va_start
, va_end
함수를 이용해 가변인자를 다룰 수 있다.
먼저 활용 예시를 보도록 하자
#include <stdio.h>
#include <stdarg.h>
void testit (int i, ...)
{
va_list argptr; // 가변인자 리스트를 가리키는 포인터
va_start(argptr, i); // 가변인자 리스트 포인터를 첫 주소로 초기화
if (i == 0)
{
// int 타입 사이즈 만큼의 데이터를 반환하고, 포인터를 sizeof(int) 만큼 이동
int n = va_arg(argptr, int);
printf("%d\n", n);
}
else
{
// char * 타입 사이즈 만큼의 데이터를 반환하고, 포인터를 sizeof(char *) 만큼 이동
char *s = va_arg(argptr, char*);
printf("%s\n", s);
}
// argptr = NULL 로 가변인자 사용을 끝마침을 표시
va_end(argptr);
}
함수에서 두 번째 인자로 사용되는 '...'이 가변 인자 혹은 가변 파라미터라고 불리는 것이다.
매개변수로 아무것도 넘겨주지 않을 수도 있고, 혹은 여러 개를 넘겨줄 수도 있다.
가변인자들은 연속된 메모리 공간에 할당이 되어있다.
따라서 해당 가변인자를 활용하기 위해서 함수의 첫 번째 인자의 위치를 알아야한다. (다른 매개변수들과 마찬가지로 함수가 실행되고 스택에 위치)
각 가변 인자의 시작 주소를 가리킬 포인터이다.
AMD64 아키텍쳐에서는 위와 같은 구조체 배열로 정의되어 있다.
reg_save_area
에서 다음으로 사용 가능한 범용 인수 레지스터가 저장되는 위치까지 오프셋을 바이트 단위로 보유한다. 모든 인수 레지스터가 소진된 경우 값 48(6*8)로 설정된다.reg_save_area
에서 다음으로 사용 가능한 부동 소수점 인수 레지스터가 저장되는 위치까지 오프셋을 바이트 단위로 보유한다. 모든 인수 레지스터가 소진된 경우 값 304(68+1616)로 설정된다.#include <stdarg.h>
void va_start(va_list ap variable_name);
va_list
로 만든 포인터va_start
는 va_list
로 만들어진 포인터에게 고정인자(variable_name)의 주소를 가르쳐주어 va_list
의 값을 초기화시켜준다.
#include <stdarg.h>
void va_end(va_list arg_ptr);
va_end
는 va_list
인 ap
의 값을 NULL
로 변경한다.
즉, 사용한 가변인자 변수를 끝낼때 사용한다.
#include <stdarg.h>
var_type va_arg(va_list ap, var_type);
va_arg
는 va_list
에 저장된 var_type
값을 검색해 반환하고, va_list
에서 다음 인수를 가리키도록 va_list
의 주소를 이동시켜 다음 인수가 시작되는 위치로 변경시킨다.
즉, 특정 가변인자를 가리키고 있는 va_list
의 포인터를 다음 가변인자로 이동시키고 기존에 가리키고 있던 값을 var_type
으로 캐스팅하여 반환한다
아래 그림에서 va_list
가 va_arg(ap, int)
에 의해 다음 가변인자로 이동한 모습이다.
var_type
의 경우 char, short의 경우 int로, flaot의 경우 double로 사용하고 형 변환을 해주어야 한다.
EX>
char ch = (char) va_arg(ap, int);
#include <stdarg.h>
void va_copy(va_list dest, va_list src);
va_copy
는 va_start
를 dest
에 적용한 후 dest
를 src
의 사본으로 초기화해준다.