Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

C++ 가변 인수(Variable Arguments) 완벽 정리: stdarg.h 활용 방법

프로그래밍을 하다 보면 함수에 전달되는 인수의 개수가 미리 정해져 있지 않고, 상황에 따라 유동적으로 변해야 하는 경우가 있습니다. 예를 들어 어떤 함수는 인수를 2개 받아야 하고, 또 다른 호출에서는 같은 함수에 5개의 인수를 넘겨야 할 수도 있죠.

C/C++ 언어는 이러한 요구를 해결하기 위해 가변 인수(variable arguments) 기능을 제공합니다. 이를 활용하면 개발자가 필요에 따라 임의 개수의 매개변수를 받을 수 있는 함수를 직접 정의할 수 있습니다.

가변 인수 함수의 기본 구조

가변 인수를 지원하는 함수는 다음과 같은 형태로 선언합니다.

int func(int, ... ) {
   .
   .
   .
}
int main() {
   func(1, 2, 3);
   func(1, 2, 3, 4);
}

여기서 주목해야 할 점은 두 가지입니다.

  • 함수 func()의 마지막 매개변수는 생략 부호(ellipsis), 즉 점 세 개(...)로 표현됩니다.
  • 생략 부호 바로 앞에는 항상 int 타입의 매개변수가 위치하며, 이 값은 실제로 전달된 가변 인수의 총개수를 나타냅니다.

가변 인수 구현 단계

가변 인수 기능을 사용하려면 stdarg.h(C++에서는 cstdarg) 헤더 파일이 필요합니다. 이 헤더 파일은 가변 인수를 처리하기 위한 함수와 매크로를 제공하며, 일반적으로 다음 순서대로 구현합니다.

  1. 마지막 매개변수를 생략 부호(...)로 정의하고, 그 바로 앞에는 인수의 개수를 나타내는 int 매개변수를 배치합니다.

  2. 함수 내부에 va_list 타입의 변수를 생성합니다. 이 타입은 stdarg.h 헤더 파일에 정의되어 있습니다.

  3. int 매개변수와 va_start 매크로를 사용하여 va_list 변수를 인수 목록으로 초기화합니다. va_start 매크로 역시 stdarg.h에 정의되어 있습니다.

  4. va_arg 매크로와 va_list 변수를 이용해 인수 목록의 각 항목에 차례대로 접근합니다.

  5. 모든 처리가 끝나면 va_end 매크로를 호출하여 va_list 변수에 할당된 메모리를 정리합니다.

예제 코드: 가변 인수로 평균 구하기

위 단계를 실제로 적용하여, 개수에 상관없이 여러 개의 정수를 전달받아 그 평균을 반환하는 간단한 함수를 작성해 보겠습니다.

#include <iostream>
#include <cstdarg>
using namespace std;

double average(int num,...) {
   va_list valist;
   double sum = 0.0;
   int i;

   va_start(valist, num); // num개의 인수를 위해 valist 초기화

   for (i = 0; i < num; i++) { // valist에 할당된 모든 인수에 접근
      sum += va_arg(valist, int);
   }

   va_end(valist); // valist를 위해 예약된 메모리 정리

   return sum/num;
}

int main() {
   cout << "Average of 2, 3, 4, 5 = "<< average(4, 2,3,4,5) << endl;
   cout << "Average of 5, 10, 15 = "<< average(3, 5,10,15)<< endl;
}

실행 결과

Average of 2, 3, 4, 5 = 3.5
Average of 5, 10, 15 = 10

정리 및 주의 사항

위 예제에서 average() 함수는 첫 번째 인수로 가변 인수의 개수를 받고, 이후 전달된 정수들을 모두 더한 뒤 개수로 나누어 평균을 계산합니다. 이처럼 가변 인수 기능을 사용하면 하나의 함수로 다양한 개수의 입력을 유연하게 처리할 수 있습니다.

다만 몇 가지 주의할 점이 있습니다. 첫째, va_arg 매크로로 값을 읽을 때는 올바른 데이터 타입을 지정해야 하며, 잘못된 타입을 지정하면 정의되지 않은 동작(undefined behavior)이 발생할 수 있습니다. 둘째, 전달된 인수의 개수를 함수 내부에서 알 수 있는 방법이 없기 때문에, 예제처럼 별도의 int 매개변수로 개수를 함께 넘겨주는 것이 일반적입니다. 마지막으로, 현대 C++에서는 타입 안전성이 보장되는 가변 인수 템플릿(variadic templates)이나 initializer_list를 사용하는 것이 권장되므로, 가능하다면 이러한 대안도 함께 고려해 보시기 바랍니다.