26.1 The <stdarg.h> Header: Variable Arguments


printf와 scanf 함수는 특이하게 인자의 개수에 대한 제한이 없는데, <stdarg.h> 헤더에서는 나만의 함수에서도 가변 숫자의 매개변수를 사용할 수 있는 기능을 제공한다.


<stdarg.h> 헤더는 한개의 타입(va_list)을 선언하고, 몇개의 매크로를 정의한다. C89에서는 va_start, va_arg, va_end의 세개의 매크로가 있다. C99에서는 function-like 매크로인 va_copy가 추가되었다.


integer 인자들(숫자 제한 없이) 중에서 가장 큰 수를 찾는 max_int 함수를 써 보자. max_int 호출은 다음과 같이 이뤄진다:


max_int(3, 10, 30, 20)


첫번째 인자는 추가로 몇개의 인자가 있는지를 지정한다. 위와 같이 max_int를 호출하면 함수는 30(10, 30, 20 중 가장 큰 수)을 리턴할 것이다.


int max_int(int n, ...)   /* n must be at least 1 */

{

    va_list ap;

    int i, current, largest;


    va_start(ap, n);

    largest = va_arg(ap, int);


    for (i = 1; i < n; i++) {

        current = va_arg(ap, int);

        if (current > largest)

            largest = current;

    }

    

    va_end(ap);

    return largest;

}


매개변수 리스트의 ... 기호(ellipsis로 불림)는 매개변수 n 뒤에 가변 숫자의 추가적인 매개변수가 뒤따른다는 것을 나타낸다. 

max_int의 body는 va_list 타입의 변수를 선언하면서 시작한다.


va_list ap;

이 변수를 선언하는 것은 max_int가 n 뒤에 따라오는 인자들에 접근하기 위해서 필수적이다.


va_start(ap, n);

위 statement는 가변 길이의 인자가 어디서 시작되는지 지정한다(이 경우, n 바로 뒤). 가변 개수 인자를 갖는 함수는 반드시 최소 하나의 "normal" 매개변수를 가져아 하고, 생략 기호는 매개변수 리스트의 가장 끝(마지막 노멀 파라미터 뒤에)에 와야 한다.


largest = va_arg(ap, int);

위 statement는 max_int의 두번째 인자(n 바로 뒤에 오는 것)를 읽어들이고, 그것을 largest에 할당한다. 그리고 자동으로 다음 인자로 진행한다. int는 max_int의 두번째 인자가 int type을 갖는 것으로 기대한다는 의미이다.

current = va_arg(ap, int);

루프 내에 있는 위 statement는 max_int의 남은 인자를 하나씩 하나씩 읽어들이게 된다.


va_end(ap);

위 statement는 함수 리턴 전에 "clean up" 하기 위해 필요하다. (또는 반환하는 대신에 va_start를 호출해서 인자 리스트를 처음부터 다시 탐색할 수도 있다).


va_copy 매크로는 src(a va_list value)를 dest(a va_list value)에 복사한다. va_copy는 src를 dest로 복사하기에 앞서 여러 번 va_arg를 호출할 수 있다는 점에서 유용하다. va_copy를 호출함으로써 인자 리스트를 기억할 수 있고 같은 지점으로 돌아와서 인자(그리고 그 뒤에 있는 인자들도)를 다시 탐색할 수 있다.

 각각의 va_start나 va_copy 호출은 va_end와 짝을 이뤄야 하며, 같은 함수 내에 나타나야 한다. va_arg의 호출은 반드시 va_start(또는 va_copy) 호출과 그와 짝을 이루는 va_end호출 사이에서 이뤄져야 한다.


Calling a Function with a Variable Argument List

가변 인자 함수 호출하기


가변 인자 리스트 함수를 호출하는 것은 본질적으로 위험한 작업이다. printf와 scanf함수 사용시에 이미 보았지만, 잘못된 인자를 전달하는 것은 매우 위험하다. 다른 가변 길이 인자 함수도 그와 동일하게 민감하다. 주된 어려움은 가변 인자 함수가 인자의 개수나 형식을 알 방법이 없다는 것이다. 그러한 정보가 함수로 전달되며, 또는(and/or) 함수에 의해 가정된다. max_int 함수는 첫번째 인자에 의존해서 추가 인자가 몇 개나 올지를 알 수 있다. 또 인자들의 형식이 int라고 가정한다. printf와 scanf와 같은 함수는 추가적인 인자와 각각의 타입을 나타내 주는 format string에 의존한다.

 또 다른 문제는 NULL을 인자로 전달하는 문제이다. NULL은 보통 0으로 정의된다. 그런데 0이 가변 인자로 전달되는 경우 컴파일러는 그것을 integer 형식이라고 간주한다. 해결책은 cast로 (void*) NULL 또는 (void*) 0 이라고 표기하는 것이다.

+ Recent posts