C 언어 | 고급 기능 | 가변 인수

printf() 함수와 같은 다른 수의 매개 변수를 받을 수 있는 함수를 만드는 방법을 설명한다.

줄임표와 가변 인수 정렬

지금까지의 설명에서 C 언어의 기본 문법과 사양들 봐왔지만, 여전히 printf() 함수와 같은 것을 만드는 것을 생각하면 어떤 의문이 제기된다. printf()와 scanf() 함수처럼 C 언어의 표준 함수에는 인수를 동적으로 얻을 수 있는 것이 존재한다. 예를 들어 printf() 함수를 보면, printf ("...")으로도 printf("...", variable)으로도 컴파일할 수 있다. 이러한 일반적인 함수는 어떻게 만들까?

인수를 가변 개수로 받을 수 있는 동적 인수를 실현하려면 3개의 점으로 구성된 줄임표(…)을 사용한다. 가변 인수를 다루는 함수의 대표적인 예로 printf() 함수는 다음과 같이 선언되어 있다.

printf() 함수

int printf( const char *format ,...);

이것은 첫번째 인수인 format은 반드시 서식 제어 문자열을 지정하지 않으면 안되지만, 두번째 인수 이후는 자유롭게 지정할 수 있다는 것을 나타낸다. 따라서, 줄임표는 인수의 마지막에 지정할 수 있다. 줄임표 후 명시적 인수가 나타나서는 안된다.

이것으로 가변 인자를 받을 수는 있지만, 문제는 함수 본문에 나열된 인수를 얻는 방법이다. 인수의 수와 형은 함수가 호출될 때까지 알 수 없으므로, 매개 변수로 받을 수 없다. 그래서 가변 인수 열를 얻으려면 C 언어가 제공하는 표준 라이브러리에 정의되어 있는 매크로 함수를 사용한다.

인수의 취득에는 va_start() 매크로 함수, va_arg() 매크로 함수, va_end() 매크로 함수의 3개의 매크로 함수를 사용한다. 이 매크로를 사용하려면 stdio.h 헤더와 stdarg.h 헤더 파일을 포함해야 한다.

va_start() 메크로 함수

void va_start( va_list arg_ptr, prev_param );

va_arg() 메크로 함수

type va_arg( va_list arg_ptr, type );

va_end() 메크로 함수

void va_end( va_list arg_ptr );

모든 매크로 함수로 지정하는 arg_ptr라는 인수는 va_list 구조체의 변수이다. va_list 구조체는 stdarg.h에서 선언되는 형태로 매크로 정보원이 된다. 개발자가 이 구조체의 정의에 대해 알 필요가 없다. 이 변수는 매크로를 사용하는 것이며, 개발자가 멤버를 직접 조작하는 것이 허용되지 않기 때문이다.

우선, 먼저 va_start () 매크로 함수를 호출하여 va_list 구조체 변수를 초기화해야 한다. 두번째 인수의 prev_param에는 가변 인수 열의 직전의 매개 변수의 이름을 지정한다. 예를 들어, printf() 함수의 선언하면 줄임표의 직전의 매개 변수인 format을 여기에 지정한다. prev_param이 register 기억 클래스에서 선언되는 경우는 이 매크로의 동작은 정의되지 않는다.

prev_param에 지정하는 매개 변수는 함수의 정의에 따라 달라진다. 매크로 함수는 지정된 매개 변수 다음에서 얻는 가변 인수 열이다고 판단하는 것이다. 다음과 같은 함수 정의시 va_start()의 prev_param에 지정하는 매개 변수는 다음과 같이 될 것이다.

void Function(int iValue , ...) → va_start(arg_ptr , iValue)

void Function(int iValue , char * pStr , ...) → va_start(arg_ptr , pStr)

초기화가 끝나면 va_arg() 매크로 함수로 인수를 얻을 수 있다. 두번째 인수의 type에는 인수의 형을 지정한다. 이 매크로 함수는 va_list 형 구조체 변수의 정보를 바탕으로 지정한 형의 값을 가져온다. 동시에 매크로 type에 지정된 형 정보에서 va_list를 나타내는 인수 위치를 이동시켜, 다음 인수가 시작되는 위치를 가리키도록 처리한다. 즉, va_arg() 매크로 함수를 호출할 때마다 다음 인수를 얻을 수 있다.

va_arg() 매크로 함수를 이용하기 위해서는 가변 인수 열에 주어진 인수의 개수와 인수의 형태를 알 필요가 있다. printf() 함수는 서식 제어 문자열에서 인수의 수와 형태를 인식하고 있는 것이다. 예를 들어 가변 인수 열에 부동 소수점과 문자열에 대한 포인터가 순서대로 부여하는 경우, 다음과 같이 얻을 수 있다.

fValue = va_arg(arg_ptr , float);
pStr = va_arg(arg_ptr , char *);

마지막으로, 처리가 끝나면 va_end() 매크로를 사용하여 arg_ptr을 해제한다. 이것으로 가변 개수의 인수를 취득하기 위한 일련의 처리가 종료된다. 이러한 3개의 매크로를 사용하여 동적 인수에 액세스할 수 있다.

코드1

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

void DynamicParameter(int arg_num , ...) {
 va_list args;
 int iValue , iCount;

  if (arg_num < 1) return;

  va_start(args , arg_num);

 for (iCount = 0 ; iCount < arg_num ; iCount++) {
    iValue = va_arg(args , int);
    printf("제 %d 인수 = %d\n" , iCount + 2 , iValue);
  }

 va_end(args);
}

int main() {
  DynamicParameter(4 , 10 , 20 , 30 , 40);
  return 0;
}

코드1는 가변 개수의 인수를 받는 함수 DynamicParameter() 함수를 작성하고 있다. 이 함수는 제 1 인수 이후에 옵션 인수의 개수를 지정한다. 동적 인수는 va_arg() 매크로를 여러번 호출할 것인가를 결정해야하기 때문에 인수의 개수를 알 필요가 있다. printf() 함수는 서식 제어 문자열 인수의 수를 분석하고 있다.

DynamicParameter()는 가변 인수를 얻기 위해, arg_num에서 그 인수의 수를 얻는다. arg_num가 1 이하이면 그 이하의 인수가 없는 것을 나타내므로, 아무것도 하지 않고 제어를 반환한다. 최초의 va_start()에는 va_list를 초기화하고, 그 후에 for 루프으로 va_arg()에서 숫자 형식으로 인수 값을 받고 있다.

이러한 동적으로 인수를 얻는 함수는 매우 범용적인 기능을 제공할 수 있지만, 인수의 해석이나 복잡한 기능의 제공은 필요 이상의 계산을 필요로 하기 때문에 처리 부담이 있다는 문제도 있다. 따라서 이러한 함수를 만드는 것은 극히 희소하다고 할 수 있다.




최종 수정 : 2017-11-26