C언어에서 stdarg.h 헤더를 사용하면 함수 선언 시 가변 인자(Variable arguments)를 가질 수 있다.
stdarg.h 헤더파일은 자료형으로 va_list를 가지며, 함수로 va_start, va_arg, va_end 등의 함수를 포함하고 있다.
아래의 예를 살펴보면 이해 할 수 있을 것이다.
#include<stdio.h>
#include<stdarg.h>
void vaPrintf(const char* sentence, ...){
int i;
va_list values;
va_start(values, sentence);
unsigned long long int iInt64;
double* iDouble_p;
for(i=0;;i++){
if(sentence[i] == 0) break;
else if(sentence[i] == '%'){
i++;
switch(sentence[i]){
case 'd' : printf("%d", va_arg(values, int));
break;
case 'c' : printf("%c", va_arg(values, unsigned int));
break;
case 'f' : printf("%f", (float)va_arg(values, double));
break;
case 's' : printf("%s", va_arg(values, char*));
break;
case 'x' : printf("%x", va_arg(values, unsigned int));
break;
case 'l' : if(sentence[i+1] == 'd'){
printf('%ld", va_arg(values, unsigned long long int));
}
else if(sentence[i+1] == 'f'){
printf("lf", va_arg(values, double));
}
else{
printf("ERROR: invalid format\n");
}
break;
defaut : printf("ERROR: invalid format\n");
break;
}
}
else{
printf("%c", sentence[i]);
}
}
}
void main(){
vaPrintf("vaPrintf test\n");
vaPrintf("int 1 = %d\n", (int)1);
vaPrintf("float 1.5 = %f\n", (float)1.5);
vaPrintf("double 1.5 = %lf\n", (double)1.5);
}
주의 해야할 점은 va로 float 을 넘기게 되면 넘어갈 때 double 형태로 넘어가게 된다고한다.(short, char 등도 int로 넘어간다.)
따라서 va_arg(va_list _list, type _t) 에서 type은 float 이 될 수 없다.
다만, function_call 시 float 을 인자로 넘긴다면, 이와같이 va_arg(_list, double) double 로 지정하더라도 할당 받는 변수의 형태에 맞춰 자동으로 float 으로 변환이 된다.(할당 받는 변수가 float 이면 float으로 자동으로 캐스팅/ double 이면 double로)
이와 같은 이유로, printf 함수에서도 double 자료형을 출력할 떄, %f / %lf 아무거나 써도 똑같이 double 형 캐스팅된 값을 출력하게 된다.
추가적으로 ARMCC 의 경우, 가변인자로 double/unsigned long long int 등 32bit 을 넘어가는 데이터가 제대로 넘어가지 않는 오류를 발견했다.
넘기기 전에 32비트씩 나누어 넘기고, 받아서 다시 data type을 바꿔주는 것이 좋을 듯 하다.