Log를 저장하는 클래스를 생성하기 전에 가변 인자 목록을 사용함수를 이용해야 할 것 같은데, 기본적인 동작의 이해를 위해 작성.
// ParserTest.cpp : 이 파일에는 'main' 함수가 포함됩니다. 거기서 프로그램 실행이 시작되고 종료됩니다.
//
#include <Windows.h>
#include <iostream>
#include <crtdbg.h>
// main에서 호출하는 실행 함수
void VariadicParamTest();
// main -> VariadicParamTest()에서 호출
void VariadicFunc(int first, int n,...);
// 가변 인자 출력을 위한 함수
// main -> VariadicParamTest() -> PrintVariadicArgsInfo()
void PrintVariadicArgsInfo(const WCHAR* msg, ...);
int main()
{
_wsetlocale(LC_ALL, L"korean");
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
VariadicParamTest();
return 0;
}
void VariadicParamTest()
{
WCHAR first = 0x1111;
short second = 0x2222;
int third = 0x33333333;
__int64 fourth = 0x1234'5678'90ab'cdef;
VariadicFunc(100, 1200, first, second, third, fourth, second, first);
}
// 가변 인자 사용 함수.
void VariadicFunc(int first, int n,...)
{
// va_start, va_list 매크로로 들어온 매개변수에 대한 접근을 한다.
// 접근하기 위해서는 va_list를 사용해야한다.
va_list argPtr = nullptr;
// ★가변 매개변수를 사용하기 전에 필수 매개변수를 이용해서 포지션을 잡아줘야한다.
// +-----------------------------------------------------------------+
// |
// | (param1, param2, 마지막 param3, ... (가변)) 일 때
// | .param3을 등록해줘야한다.
// |
// | 가변 인자를 사용하기 위해선 필수 매개변수가 존재해야한다.
// | 최소 : Func(type param, ...)
// |
// |
// | [순서]
// |
// | 1.★
// | va_Start(va_list, param) --> va_list는 param으로 전달된
// | 매개변수의 다음 주소 (즉 &가변 매개변수[0]) 를 가리킴
// |
// | 2.★
// | Type userVar = va_arg(va_list, Type)
// | va_list가 가지고 있는 주소의 값을 Type으로 캐스팅 후 대입
// | 그 후에 포인터 크기만큼을 옮겨주지만
// | 32bit에서 64비트 데이터의 경우 2개의 포인터크기만큼을 올려준다.
// |
// |
// | 3.★
// | va_end(va_list)
// | va_list의 값을 nullptr로 밀어준다.
// |
// |
// | 어떤 매개변수든 넣을 수 있지만, 각 타입에 따른 처리는
// | 사용자가 처리를 할 수 있도록 만들어야한다.
// |
// |-----------------------------------------------------------------|
// | vwprintf(const wchar* msgFormat, ...)
// | 위의 va_list, va_start, va_end를 활용하는 것은 동일
// |
// | 활용은 포맷과 가변 인자를 넣어주면 OK
// | vwprintf(msgFormat, va_list)
// |
// | va_list를 순회하면서 Format에 맞게 출력
// | Format의 매개변수 수 < 가변 매개 변수 수 -> 전부 출력 안됨
// | Format의 매개변수 수 > 가변 매개 변수 수 -> 쓰레기 데이터 출력 |
// |
// +-----------------------------------------------------------------+
//va_start(argPtr, first);
va_start(argPtr, n);
// va_arg(va_list, type)
// va_start을 통해
int i = 0;
while (true)
{
void* prevPtr = argPtr;
// WCHAR -> SHORT -> INT -> _INT64 -> SHORT -> WCHAR 형태로 출력
switch (i)
{
case 0:
case 5:
{
WCHAR value = va_arg(argPtr, WCHAR);
PrintVariadicArgsInfo(L"Value : 0x%0x, prevPtr : [0x%p], ptr : [0x%p]\n", value, prevPtr, argPtr);
//PrintVariadicArgsInfo(L"Value : 0x%0x, prevPtr : [0x%p], ptr : [0x%p]\n", va_arg(argPtr, WCHAR), prevPtr, argPtr);
}
break;
case 1:
case 4:
{
short value = va_arg(argPtr, short);
PrintVariadicArgsInfo(L"Value : 0x%0x, prevPtr : [0x%p], ptr : [0x%p]\n", value, prevPtr, argPtr);
//PrintVariadicArgsInfo(L"Value : 0x%0x, prevPtr : [0x%p], ptr : [0x%p]\n", va_arg(argPtr, short), prevPtr, argPtr);
}
break;
case 2:
{
int value = va_arg(argPtr, int);
PrintVariadicArgsInfo(L"Value : 0x%0x, prevPtr : [0x%p], ptr : [0x%p]\n", value, prevPtr, argPtr);
//PrintVariadicArgsInfo(L"Value : 0x%0x, prevPtr : [0x%p], ptr : [0x%p]\n", va_arg(argPtr, int), prevPtr, argPtr);
}
break;
case 3:
{
LONGLONG value = va_arg(argPtr, __int64);
// 64바이트 출력을 위해선 %llx를 사용하자!
PrintVariadicArgsInfo(L"Value : 0x%0llx, prevPtr : [0x%p], ptr : [0x%p]\n", value, prevPtr, argPtr);
//PrintVariadicArgsInfo(L"Value : 0x%0llx, prevPtr : [0x%p], ptr : [0x%p]\n", va_arg(argPtr, __int64), prevPtr, argPtr);
}
break;
default:
// 사용자가 따로 매개변수를 넘어서서 접근했을 때 오류가 발생하는지의 확인을 위해 추가함
PrintVariadicArgsInfo(L"Value : 0x%0x, prevPtr : [0x%p], ptr : [0x%p]\n", va_arg(argPtr, WCHAR), prevPtr, argPtr);
break;
}
if (prevPtr == argPtr)
break;
i++;
}
va_end(argPtr);
}
void PrintVariadicArgsInfo(const WCHAR* msg, ...)
{
// 생성하고
va_list vaList;
// 열어주고
va_start(vaList, msg);
int messageCount = vwprintf_s(msg, vaList);
if (messageCount < 0)
{
wprintf(L"MessageCount < 0 이면 Error입니다.");
}
// 닫아주고
va_end(vaList);
}
// 32bit 기준 8byte (__int64) 도 매개변수로 저장할 때는 4*2칸의 공간을 그대로 할당해버림
가변 인자 사용을 위해선 (필수 매개변수 + ...) 의 형태로 사용
va_list / va_start / va_end
va_list는 가변 인자에 접근 위한 포인터 (char*로 정의되어 있음.)
★ ( 32bit 환경에서 __int64값이 필수 매개변수 주소라면 포인터 크기(4) * 2 만큼 움직임) ☆
va_start(va_list, 필수 매개변수)
- va_list = (필수 매개변수 주소) + 포인터 크기
Type 변수 = va_arg(va_list, Type)
- va_list를 (Type)한 값을 변수에 대입 한 후 va_list += 포인터 크기
==> 변수 = ((Type*)va_list); va_list += 포인터 크기
va_end(va_list)
vwprintf(const wchar* format, ...)
- va_list를 순회하면서 Format에 맞게 출력
- Format의 매개변수 수 < 가변 매개 변수 수 -> 전부 출력 안됨
- Format의 매개변수 수 > 가변 매개 변수 수 -> 쓰레기 데이터 출력
결국 사용할 때 프로그래머가 알아서 잘! 사용해야 된다는 게 내가 느낀 것이다.
va_arg(__int64) 읽기 전

va_arg(__int64) 읽은 후

va_arg(__int64) 전 / 후 메모리
