가변 인자

가변 인자 (Variadic arguments)

함수에 넘길 인자의 개수를 상황에 따라 바꾸고 싶다면 **가변 인자 함수(variadic function)**를 쓰면 돼요. printf처럼 호출할 때마다 인자의 개수가 달라지는 함수가 대표적이에요.

출처: cppreference

본문

가변 인자 함수는 서로 다른 개수의 인자로 호출될 수 있는 함수예요. 프로토타입이 있는 함수 선언만 가변 인자일 수 있어요. 이는 매개변수 목록의 맨 마침에 와야 하는 ... 형태의 매개변수로 나타내요 (C23 이전에는 뒤에 적어도 하나의 이름 붙은 매개변수가 반드시 있어야 했어요). 생략 기호 매개변수와 그 앞 매개변수는 ,로 구분돼야 해요.

// Prototyped declaration
int printx(const char* fmt, ...); // function declared this way
printx("hello world");     // may be called with one
printx("a=%d b=%d", a, b); // or more arguments

int printz(...); // OK since C23 and in C++
// Error until C23: ... must follow at least one named parameter

// int printy(..., const char* fmt); // Error: ... must be the last
// int printa(const char* fmt...);   // Error in C: ',' is required; OK in C++

함수 호출 시, 가변 인자 목록의 일부인 각 인자는 **기본 인자 승격(default argument promotions)**이라는 특별한 묵시적 변환을 거쳐요.

가변 인자를 쓰는 함수 본문에서는 <stdarg.h> 라이브러리 시설로 이 인자들의 값을 접근할 수 있어요.

헤더 시설
<stdarg.h> va_start · va_arg · va_copy · va_end · va_list

참고

옛 스타일(프로토타입 없는) 함수 선언은 뒤따르는 함수 호출이 어떤 개수의 인자든 쓰는 걸 허용하지만, (C89 기준으로) 가변 인자일 수는 없어요. 그런 함수의 정의는 고정된 개수의 매개변수를 지정해야 하고 stdarg.h 매크로는 쓸 수 없어요.

// old-style declaration, removed in C23
int printx(); // function declared this way
printx("hello world");     // may be called with one
printx("a=%d b=%d", a, b); // or more arguments
// the behavior of at least one of these calls is undefined, depending on
// the number of parameters the function is defined to take

예제

여기서는 시간 스탬프를 붙여 로그를 찍는 tlog 함수를 만들어, 가변 인자와 vprintf가 어떻게 함께 쓰이는지 확인해 볼게요.

#include <stdio.h>
#include <time.h>
#include <stdarg.h>

void tlog(const char* fmt,...)
{
    char msg[50];
    strftime(msg, sizeof msg, "%T", localtime(&(time_t){time(NULL)}));
    printf("[%s] ", msg);
    va_list args;
    va_start(args, fmt);
    vprintf(fmt, args);
    va_end(args);
}

int main(void)
{
   tlog("logging %d %d %d...\n", 1, 2, 3);
}

출력:

[10:21:38] logging 1 2 3...

더 알아보기

  • 가변 인자를 실제로 꺼내 쓰는 방법은 <stdarg.h> 문서에서 va_list, va_start, va_arg를 차례로 보면 돼요.
  • 가변 인자가 기본 인자 승격을 거치는 이유는 묵시적 변환 문서에서 설명해요.
  • C++의 가변 인자(variadic arguments)도 같은 아이디어를 다루니 비교해 보세요.