가변인자를 string 으로 사용하기
C/C++에는 함수의 인자를 가변으로 다룰 수 있는 기능이 있는데, 이때 사용하는 것이 va_list, va_start, va_arg, va_end다. 여기서 va_list는 타입이고 나머지 셋은 매크로다. 함수가 아니다.
이 가변인자를 문자열 형태로 사용하려고 하면 문제가 많아서, 테스트하는 코드를 만들었다.
2026년 수정 안내 원래 실었던 코드는 그대로는 컴파일되지 않았고(
A123등이 문자열 리터럴이 아닌 미정의 식별자였다), sentinel로 쓴NULL에도 이식성 문제가 있었다. 코드를 바로잡고, C++이라면 애초에 쓰지 않는 편이 나은 이유를 덧붙인다.
C 스타일 가변인자
#include <cstdarg>
#include <cstdio>
void TestImpl( const char* first, ... )
{
va_list ap;
va_start( ap, first );
for ( const char* p = first; p != nullptr; p = va_arg( ap, const char* ) )
{
printf( "code : %s\n", p );
}
va_end( ap );
}
// 호출부에서 마지막에 종료 표시를 자동으로 붙여준다.
#define Test( ... ) TestImpl( __VA_ARGS__, static_cast<const char*>( nullptr ) )
int main()
{
Test( "A123", "A234", "A345" ); // 반드시 문자열 리터럴이어야 한다
return 0;
}
알아둘 점 세 가지
1. std::string은 ...으로 넘길 수 없다.
가변인자로는 trivially copyable하지 않은 타입을 넘길 수 없다. std::string은 생성자/소멸자를 가진 클래스라 여기에 해당하며, 넘기더라도 정의되지 않은 동작이다. 그래서 const char*로 받아야 하고, 호출부에 std::string이 있다면 .c_str()을 붙여야 한다.
2. 종료 표시로 NULL을 그냥 쓰면 안 된다.
NULL은 구현에 따라 0(int)으로 확장될 수 있다. 그러면 64비트 환경에서 4바이트만 스택에 올라가는데 va_arg는 8바이트 포인터로 읽으므로 값이 깨진다. nullptr을 쓰거나, C라면 (const char*)NULL처럼 명시적으로 캐스팅해야 한다.
3. 개수를 알 방법이 없다.
가변인자는 몇 개가 넘어왔는지 스스로 알 수 없다. 그래서 위처럼 마지막에 sentinel을 붙이거나, printf처럼 첫 인자에 개수/포맷을 넘겨야 한다. 매크로로 sentinel을 자동으로 붙인 이유가 이것이다.
C++이라면 가변 템플릿을 쓰자
위 방식은 타입 안전성이 전혀 없다. 인자를 하나라도 잘못 넘기면 컴파일은 되고 런타임에 깨진다. C++11 이후라면 가변 템플릿(variadic template) 을 쓰는 편이 모든 면에서 낫다.
#include <iostream>
#include <string>
template<typename... Args>
void Test( const Args&... args )
{
( ( std::cout << "code : " << args << "\n" ), ... ); // C++17 폴드 표현식
}
int main()
{
std::string dynamic = "A456";
Test( "A123", "A234", "A345", dynamic ); // std::string도 그대로 넘어간다
return 0;
}
- 타입이 컴파일 시점에 검사된다.
std::string을 포함해 어떤 타입이든 넘길 수 있다.- sentinel이 필요 없다. 개수는
sizeof...(args)로 알 수 있다.
목록을 그냥 순회하기만 하면 되는 경우라면 std::initializer_list가 더 간단하다.
void Test( std::initializer_list<std::string_view> codes )
{
for ( auto code : codes )
std::cout << "code : " << code << "\n";
}
Test( { "A123", "A234", "A345" } );
댓글 남기기