C언어 - 가변인자(variadic)란? 사용법 및 예시 정리

김예찬·2024년 2월 14일
0

printf 함수를 직접 구현하면서 C언어 가변인자에 대해 공부해 보았다.

가변인자란?

가변인자는 함수를 정의할 때, 그 함수가 받는 인자의 개수를 고정하지 않고 유동적으로 처리할 수 있게 해준다. 이는 함수를 더욱 유연하게 만들어주며, 특히 많은 수의 인자를 처리해야 하는 함수를 작성할 때 유용하다.

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

// 가변인자를 이용하여 정수들의 합을 계산하는 함수
int sum(int count, ...) {
    va_list ptr;
    va_start(ptr, count);

    int total = 0;
    for (int i = 0; i < count; ++i) {
        int num = va_arg(ptr, int); // 가변인자로부터 정수를 가져옴
        total += num;
    }

    va_end(ptr);

    return total;
}

int main() {
    // 다양한 개수의 정수를 전달하여 합을 계산
    printf("Sum of 3, 5, 7, 9, 11: %d\n", sum(5, 3, 5, 7, 9, 11));
    printf("Sum of 2, 4, 6, 8: %d\n", sum(4, 2, 4, 6, 8));
    printf("Sum of 1, 2, 3, 4, 5, 6, 7, 8, 9, 10: %d\n", sum(10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));

    return 0;
}

위와 같이 sum 함수는 처음 인자로 정수의 개수를 받고, 이후에 나오는 인자들을 가변인자로 처리하여 합을 계산한다. 이렇게 가변인자를 사용한 함수는 임의의 개수의 인자를 전달받아 처리할 수 있다.

가변인자 구성요소

가변인자를 사용하기 위해서는 몇 가지 필수 요소가 있다. stdarg.h 헤더 파일에 정의된 va_start, va_arg, va_end와 같은 매크로들이 이에 해당한다. 이러한 매크로들을 이용하여 가변인자를 초기화하고, 각 인자에 접근하여 처리할 수 있다.

  1. stdarg.h 헤더 파일: stdarg.h 헤더 파일에는 가변인자를 다루기 위한 매크로와 함수들이 정의되어 있어 꼭 포함해줘야한다.

  2. va_list: va_list 타입은 가변인자 목록을 가리키는 일종의 포인터이다.

  3. va_start 매크로: 가변인자 목록을 초기화하는 역할을 한다. 가변인자 목록을 순회하기 전에 va_start 매크로를 사용하여 초기화해야 한다.

    int sum(int count, ...) {
        va_list ptr;
        va_start(ptr, count);

    위와 같이 마지막 고정인자를 인자로 가지는데 va_start 내부에서 마지막 고정인자의 끝 다음부분으로 주소를 이동시켜 가변인자의 시작주소를 ptr에 저장한다.

  4. va_arg 매크로: 가변인자 목록에서 다음 가변인자의 값을 가져오는 역할을 한다. 이 매크로는 가져올 인자의 타입과 가변인자 목록을 가리키는 포인터를 인자로 받는다. 가져온 값은 해당 타입으로 반환된다.

    va_arg(가변인자포인터, 가져올 타입)

    //정수
    int num;
    num = va_arg(ptr, int);
    
    //단어
    char c;
    c = (char)va_arg(ptr, int);
    
    //문자열 
    char *s;
    s = va_arg(ptr, char *);

    이때 char 타입의 인자를 받을 때에도 va_arg(ptr, int)와 같이 int 타입으로 값을 가져와야 한다는 점을 ⚠️주의하자.

    그 이유는 C 언어의 가변인자 처리 규칙에 따라, 작은 크기의 정수 타입들은 자동으로 큰 크기의 정수 타입으로 확장되므로 char 타입은 int 타입으로 자동 확장되어 전달되기 때문이라고 한다.

  1. va_end 매크로: 가변인자 처리가 끝났음을 나타낸다. 포인터에 null을 반환한다.

가변인자 사용예시(printf)

가변인자 함수의 대표적인 예시로는 C 언어의 printf 함수가 있다. printf 함수는 서식 지정자를 이용하여 출력 형식을 지정하고, 이후에 오는 가변인자들을 출력한다. 예를 들어, "Hello, %s!"와 같은 형식 문자열에서 %s는 문자열을 출력하기 위한 서식 지정자이며, 이에 대응하는 인자가 출력되게 된다.

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

//인자의 포맷 체크
void	check_format(const char *format, va_list ptr)
{
	if (*format == 'c')
	{
		
		char c = (char)va_arg(ptr, int);
		write(1,&c,1);
	}
	else if (*format == 's')
	{
		char *c = va_arg(ptr, char *);
		while(*c)
		{
			write(1,c,1);
			c++;
		}
	}
}

//%c, %s만 처리하는 간단한 printf구현
int	ft_printf(const char *format,...)
{
	va_list	ptr;

	va_start(ptr,format);

	while (*format)
	{
		if (*format == '%')
		{
			format++;
			check_format(format, ptr, &len);
			format++;
		}
		else
		{
			write(1,format,1);
			format++;
		}
	}

	va_end(ptr);
	return (1);
}

int main(void)
{
	ft_printf("Hello, %c bye",'a');
	ft_printf("Hello, %s bye","kim");
}

맺음말

매번 사용하던 printf 함수가 어떤 원리로 다양한 개수의 인자를 처리할 수 있는지 알 수 있었다.
또한 가변인자는 함수의 유연성을 높여주지만 잘못 사용할 경우 버그의 원인이 될 수 있으므로 조심스럽게 다뤄야 하며, 특히 타입 일치와 같은 주의사항을 유의해야겠다.

0개의 댓글