C++ 가변 인자 사용시 va_start 파라미터 전달 방법
아래의 코드를 Visual Studio 디버그 모드로 실행하면,
#include "stdafx.h"
#include <stdarg.h>
#include <string>
using namespace std;
void OutputText2(wstring fmt, ...);
int _tmain(int argc, _TCHAR* argv[])
{
OutputText2(L"test %s\n", L"test");
return 0;
}
void OutputText2(wstring fmt, ...)
{
const wchar_t *pFmt = fmt.c_str();
va_list args;
va_start(args, pFmt);
vwprintf(pFmt, args);
va_end(args);
}
output.c 소스 코드가 열리면서 비정상 종료를 알립니다.
Unhandled exception at 0x0F85AFBB (msvcr120d.dll) in ConsoleApplication1.exe: 0xC0000005: Access violation reading location 0xCCCCCCCC.
이유가 뭘까요? ^^ 반면에 OutputText 메서드의 첫 번째 인자를 "wchar_t *"로 바꿔주면 정상적으로 실행됩니다.
void OutputText(wchar_t *fmt, ...)
{
va_list args;
va_start(args, fmt);
vwprintf(fmt, args);
va_end(args);
}
도대체 이유가 뭘까요? ^^ 원인은 MSDN 문서를 찾아보니 나옵니다.
va_arg, va_copy, va_end, va_start
; https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/va-arg-va-copy-va-end-va-start
va_start sets arg_ptr to the first optional argument in the list of arguments that's passed to the function. The argument arg_ptr must have the va_list type. The argument prev_param is the name of the required parameter that immediately precedes the first optional argument in the argument list.
즉 va_start의 두 번째 인자로 전달되는 값은, 가변 인자 중 첫 번째 바로 이전에 오는 인자를 전달해야 하는 것입니다. 따라서 OutputText2 함수는 다음과 같이 바뀌어야 합니다.
void OutputText2(wstring fmt, ...)
{
const wchar_t *pFmt = fmt.c_str();
va_list args;
va_start(args, fmt);
vwprintf(pFmt, args);
va_end(args);
}
즉, 가변 인자가 전달되는 "스택" 기준의 앞에 있는 인자를 전달해야 하는 것입니다. 그래서 다음과 같은 설명도 나옵니다.
If prev_param is declared with the register storage class, the macro's behavior is undefined.
만약, 앞의 인자가 레지스터를 통해 전달되는 경우라면 va_start는 va_list를 잘못된 값으로 초기화할 것이고 그 이후의 동작은 예측할 수 없는 상태로 빠지는 것입니다. (대개의 경우, 비정상 종료)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]