본문 바로가기
코딩취미/C,C++

초보자를 위한 C언어 가변 인자 함수: va_list, va_start, va_end 완벽 가이드

by 브링블링 2024. 9. 13.
반응형

초보자를 위한 C언어 가변 인자 함수: va_list, va_start, va_end 완벽 가이드

C언어에서 가변 인자 함수는 인자의 개수가 고정되지 않은 경우, 다양한 개수의 인자를 받아 처리할 수 있는 강력한 기능을 제공합니다. 대표적인 예로 printf 함수가 있는데, 이 함수는 여러 개의 인자를 받아서 문자열로 출력합니다. 이러한 가변 인자 함수를 구현하기 위해서는 va_list, va_start, va_arg, va_end와 같은 매크로들을 사용해야 합니다.

 

여기에서는 이 매크로들의 역할을 자세히 설명하고, 가변 인자 함수를 작성하는 방법을 정리했습니다.

1. 가변 인자 함수란?

가변 인자 함수는 호출 시 전달되는 인자의 개수가 고정되지 않은 함수입니다. 예를 들어, printf 함수는 다양한 개수의 인자를 전달받아 처리합니다. 이를 위해 C언어는 가변 인자 함수를 지원하며, 이때 사용되는 주요 매크로는 stdarg.h 헤더 파일에 정의된 va_list, va_start, va_arg, va_end입니다.

2. va_list, va_start, va_arg, va_end의 역할

가변 인자 함수에서 가변 인자를 처리하기 위해서는 다음의 매크로들이 필요합니다.

1) va_list의 역할

va_list는 가변 인자 목록을 담는 자료형입니다. 이 변수는 가변 인자를 순차적으로 처리하는 데 사용되며, 가변 인자를 추적하는 포인터 역할을 합니다. 가변 인자 함수에서 여러 인자가 전달되었을 때, 이를 효과적으로 관리하고 각 인자를 처리하기 위해 va_list 변수가 필요합니다.

 

va_list는 가변 인자 함수에서 반드시 선언해야 하며, 이 변수를 통해 인자 목록을 순차적으로 접근할 수 있습니다. 이후, va_start와 va_arg를 통해 인자들을 하나씩 가져옵니다.

va_list args;  // 가변 인자 목록을 저장하는 va_list 선언

2) va_start의 역할

va_start는 가변 인자 목록의 처리를 시작하는 매크로입니다. 가변 인자 함수가 호출될 때, 함수는 고정된 인자들 이후에 가변 인자가 위치하는 메모리 주소를 알아야 합니다. va_start는 이를 해결하며, 가변 인자 목록의 시작 위치를 초기화해 줍니다.

 

  • va_list: 가변 인자를 관리하는 변수로, va_list 타입으로 선언한 변수입니다.
  • last_fixed_arg: 가변 인자 앞에 있는 마지막 고정 인자입니다. 이 매개변수는 가변 인자 목록의 시작 지점을 결정하는 데 필요한 정보입니다.

 

va_start(va_list, last_fixed_arg);
va_list args;  // 가변 인자 목록 선언
va_start(args, num);  // 가변 인자 처리 시작, num은 마지막 고정 인자

3) va_arg의 역할

va_arg는 가변 인자 목록에서 다음 인자를 가져오는 매크로입니다. 인자를 하나씩 처리하기 위해 이 매크로를 사용하며, 가져올 인자의 자료형을 명시해야 합니다. 가변 인자 목록은 순차적으로 접근되며, 각 인자의 타입을 정확히 지정해야만 올바른 값이 반환됩니다.

 

  • va_list: 가변 인자를 담고 있는 va_list 변수입니다.
  • type: 가변 인자의 자료형을 명시해야 하며, 이를 통해 해당 타입의 값을 가져옵니다.

 

va_arg(va_list, type);
int value = va_arg(args, int);  // 가변 인자 목록에서 int형 값을 가져옴

4) va_end의 역할

va_end는 가변 인자 목록의 처리를 종료하는 매크로입니다. 가변 인자 함수가 끝나면 반드시 va_end를 호출해야 메모리 관련 처리를 마무리할 수 있습니다. va_end는 va_start로 시작한 가변 인자 처리를 종료하며, 가변 인자 목록을 더 이상 사용할 수 없도록 합니다.

  • va_list: 가변 인자 목록을 담고 있는 변수로, 이 변수는 vs_start와 함께 사용한 va_list 변수여야 합니다.
va_end(va_list);

 

va_start로 가변 인자 처리를 시작한 후, 반드시 va_end를 호출하여 처리를 마쳐야 합니다. 그렇지 않으면 메모리 누수나 비정상적인 동작이 발생할 수 있습니다.

va_end(args);  // 가변 인자 처리 종료
반응형

3. 가변 인자 함수 작성 예시

이제 va_list, va_start, va_arg, va_end를 사용하여 가변 인자 함수를 작성하면 아래와 같습니다. 예로, 전달된 숫자들을 출력하는 가변 인자 함수를 만들었습니다.

 

  • print_numbers 함수는 고정 인자인 num과 가변 인자를 받습니다.
  • va_list args: 가변 인자 목록을 저장할 args 변수를 선언합니다.
  • va_start(args, num): 가변 인자 처리를 시작합니다. num은 가변 인자 앞의 마지막 고정 인자입니다.
  • va_arg(args, int): args에서 하나의 가변 인자를 가져오고, 그 자료형은 int로 명시합니다.
  • va_end(args): 가변 인자 처리 종료 후 메모리 처리를 마무리합니다.

 

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

void print_numbers(int num, ...) {
    va_list args;  // 가변 인자 목록 선언
    va_start(args, num);  // 가변 인자 처리 시작 (num은 고정 인자)

    for (int i = 0; i < num; i++) {
        int value = va_arg(args, int);  // 가변 인자에서 int형 값을 가져옴
        printf("%d ", value);
    }

    va_end(args);  // 가변 인자 처리 종료
    printf("\n");
}

int main() {
    print_numbers(3, 10, 20, 30);  // 3개의 숫자 전달
    print_numbers(5, 1, 2, 3, 4, 5);  // 5개의 숫자 전달
    return 0;
}
10 20 30 
1 2 3 4 5

4. 가변 인자 함수에서 주의할 점

1) 가변 인자의 개수를 정확히 관리해야 함

가변 인자 함수는 전달된 인자의 개수를 알 수 없습니다. 이를 해결하기 위해 보통 첫 번째 고정 인자로 인자의 개수를 전달하거나, 특정 형식 문자열을 전달하여 인자의 개수를 유추할 수 있도록 합니다. 예를 들어 printf 함수는 첫 번째 인자인 형식 문자열을 통해 몇 개의 가변 인자가 오는지 파악합니다.

 

2) 가변 인자의 자료형을 정확히 지정해야 함

va_arg로 인자를 가져올 때 반드시 그 인자의 자료형을 명확하게 지정해야 합니다. 그렇지 않으면 메모리 읽기 오류나 예기치 않은 동작이 발생할 수 있습니다. 예를 들어, int 형 인자를 double 형으로 읽으려고 시도하면 잘못된 결과가 나올 수 있습니다.

 

3) va_start와 va_end는 반드시 쌍으로 사용해야 함

va_start로 가변 인자 처리를 시작한 후에는 반드시 va_end를 호출하여 가변 인자 처리를 종료해야 합니다. 그렇지 않으면 메모리 누수나 비정상적인 프로그램 동작을 유발할 수 있습니다.

5. 평균 계산 함수 예시

가변 인자를 사용하여 전달된 숫자들의 평균을 계산하는 코드입니다.

 

  • calculate_average 함수는 가변 인자를 통해 전달된 숫자들의 평균을 계산합니다.
  • va_start와 va_arg를 사용하여 전달된 인자들을 모두 합산한 후, 그 값을 인자의 개수로 나누어 평균을 계산합니다.
  • va_end를 사용하여 가변 인자 처리를 종료합니다.

 

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

double calculate_average(int num, ...) {
    va_list args;
    va_start(args, num);
    
    int sum = 0;
    for (int i = 0; i < num; i++) {
        sum += va_arg(args, int);  // 가변 인자에서 int형 값을 가져옴
    }
    
    va_end(args);
    
    return (double)sum / num;
}

int main() {
    double avg1 = calculate_average(3, 10, 20, 30);  // 3개의 숫자 전달
    double avg2 = calculate_average(5, 1, 2, 3, 4, 5);  // 5개의 숫자 전달

    printf("평균 1: %.2f\n", avg1);
    printf("평균 2: %.2f\n", avg2);

    return 0;
}
평균 1: 20.00
평균 2: 3.00
반응형