printf
같이 매개변수의 개수가 정해지지 않고, 매번 함수에 들어가는 인수의 개수가 변하는 것을 가변인자
라고 한다.
함수에서 가변 인자를 정의할 때는 고정 매개변수가 한 개 이상 있어야 하며
고정 매개변수 뒤에 ...
을 붙여 매개변수의 개수가 정해지지 않았다는 표시를 해준다.
반환값자료형 함수이름(자료형 고정매개변수, ...)
{
}
#include <stdio.h>
#include <stdarg.h> // va_list, va_start, va_arg, va_end가 정의된 헤더 파일
void printNumbers(int args, ...) // 가변 인자의 개수를 받음, ...로 가변 인자 설정
{
va_list ap; // 가변 인자 목록 포인터
va_start(ap, args); // 가변 인자 목록 포인터 설정
for (int i = 0; i < args; i++) // 가변 인자 개수만큼 반복
{
int num = va_arg(ap, int); // int 크기만큼 가변 인자 목록 포인터에서 값을 가져옴
// ap를 int 크기만큼 순방향으로 이동
printf("%d ", num); // 가변 인자 값 출력
}
va_end(ap); // 가변 인자 목록 포인터를 NULL로 초기화
printf("\n"); // 줄바꿈
}
int main()
{
printNumbers(1, 10); // 인수 개수 1개
printNumbers(2, 10, 20); // 인수 개수 2개
printNumbers(3, 10, 20, 30); // 인수 개수 3개
printNumbers(4, 10, 20, 30, 40); // 인수 개수 4개
return 0;
}
10
10 20
10 20 30
10 20 30 40
va_list: 가변 인자 목록. 가변 인자의 메모리 주소를 저장하는 포인터.
va_start:
void va_start(va_list ap, variable_name);
가변 인자를 가져올 수 있도록 포인터를 설정.
va_list
로 만들어진 포인터에게 가변인자 중 첫 번째 선택적 인수(variable_name)의 주소를 가르쳐주는 매크로 함수.
va_arg(), va_copy(), va_end()에 대한 후속 호출에 대해 ap 포인터를 초기화한다.
va_list
로 만든 포인터가 담긴다.va_arg:
var_type va_arg(va_list ap, var_type);
가변 인자 포인터에서 특정 자료형 크기만큼 값을 가져옵니다.
ap로 지정된 위치에서 지정된 var_type 값을 검색하고 리스트에서 다음 인수를 가리키도록 ap 를 증가 시켜 다음 인수가 시작 되는 위치를 결정한다. 즉, 특정 가변인자를 가리키고 있는 va_list
의 포인터를 다음 가변인자로 이동시켜 주는 매크로이다.
va_list
로 만든 포인터가 담긴다.int
나 long
, double
과 같은 타입 이름이 담긴다.var_type을 설정할 때 char, short 의 경우에는 int로 대신 쓰고, flaot의 경우에는 double로 대신 쓴 이후 형 변환을 해주어야 한다.
ex) char ch = (char) va_arg(ap, int);
int num = va_arg(ap, int);를 실행하면 현재 ap에서 4바이트(int 크기)만큼 역참조하여 값을 가져온 뒤 ap를 4바이트만큼 순방향으로 이동시킵니다.
va_end:
void va_end(va_list arg_ptr);
모든 인수가 검색 된 후 va_end 는 포인터를 NULL로 다시 설정 한다. 즉, 사용한 가변인자 변수를 끝낼때 사용.
가변 인자 처리가 끝났을 때 포인터(ap)를 NULL로 초기화합니다.
#include <stdio.h>
#include <stdarg.h> // va_list, va_start, va_arg, va_end가 정의된 헤더 파일
void printValues(char *types, ...) // 가변 인자의 자료형을 받음, ...로 가변 인자 설정
{
va_list ap; // 가변 인자 목록
int i = 0;
va_start(ap, types); // types 문자열에서 문자 개수를 구해서 가변 인자 포인터 설정
while (types[i] != '\0') // 가변 인자 자료형이 없을 때까지 반복
{
switch (types[i]) // 가변 인자 자료형으로 분기
{
case 'i': // int형일 때
printf("%d ", va_arg(ap, int)); // int 크기만큼 값을 가져옴
// ap를 int 크기만큼 순방향으로 이동
break;
case 'd': // double형일 때
printf("%f ", va_arg(ap, double)); // double 크기만큼 값을 가져옴
// ap를 double 크기만큼 순방향으로 이동
break;
case 'c': // char형 문자일 때
printf("%c ", va_arg(ap, char)); // char 크기만큼 값을 가져옴
// ap를 char 크기만큼 순방향으로 이동
break;
case 's': // char *형 문자열일 때
printf("%s ", va_arg(ap, char *)); // char * 크기만큼 값을 가져옴
// ap를 char * 크기만큼 순방향으로 이동
break;
default:
break;
}
i++;
}
va_end(ap); // 가변 인자 포인터를 NULL로 초기화
printf("\n"); // 줄바꿈
}
int main()
{
printValues("i", 10); // 정수
printValues("ci", 'a', 10); // 문자, 정수
printValues("dci", 1.234567, 'a', 10); // 실수, 문자, 정수
printValues("sicd", "Hello, world!", 10, 'a', 1.234567); // 문자열, 정수, 문자, 실수
return 0;
}
:warning: GCC에서는 char형 문자일 때 va_arg 매크로에 char 대신 int를 사용해야한다.
case 'c': // char형 문자일 때
printf("%c ", va_arg(ap, int)); // int 크기만큼 값을 가져옴
break;