1 概述
在C语言编程中会遇到一些参数个数可变的函数,例如printf()这个函数,它的定义是这样的:
int printf( const char* format, ...);
它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的,例如我们可以有以下不同的调用方法:
printf("%d",i);
printf("%s",s);
printf("the number is %d ,string is:%s", i, s);
究竟如何写可变参数的C函数以及这些可变参数的函数编译器是如何实现的呢?本文就这个问题进行一些探讨.
2 实现原理
2.1 printf实现原理
该实现使用了va_start、va_arg、va_end。如下,
l va_list
va_list表示可变参数列表类型,实际上就是一个char指针.
l va_start
va_start用于获取函数可变参数列表(获取函数参数列表中可变参数的首指针)
* 输出参数ap(类型为va_list): 用于保存函数参数列表中可变参数的首指针
* 输入参数A: 为函数参数列表中最后一个固定参数。
l va_arg
va_arg用于获取当前ap所指的可变参数并将并将ap指针移向下一可变参数
* 输入参数ap(类型为va_list): 可变参数列表,指向当前正要处理的可变参数
* 输入参数T: 正要处理的可变参数的类型;
* 返回值: 当前可变参数的值;
l va_end
va_end用于结束对可变参数的处理。实际上,va_end被定义为空.它只是为实现与va_start配对(实现代码对称和"代码自注释"功能)
2.2 printf实现代码
void print(const char *fmt, ...)
{
char str[100];
unsigned int len, i, index;
int iTemp;
char *strTemp;
va_list args;
va_start(args, fmt);
len = strlen(fmt);
for (i=0, index=0; i<len; i++)
{
if (fmt[i] != '%') /* 非格式化参数 */
{
str[index++] = fmt[i];
}
else /* 格式化参数 */
{
switch(fmt[i+1])
{
case 'd': /* 整型 */
case 'D':
iTemp = va_arg(args, int);
strTemp = itoa(iTemp, str+index);
index += strlen(strTemp);
i++;
break;
case 's': /* 字符串 */
case 'S':
strTemp = va_arg(args, char*);
strcpy(str + index, strTemp);
index += strlen(strTemp);
i++;
break;
default:
str[index++] = fmt[i];
}
}
}
str[index] = '\0';
va_end(args);
}
3 Linux内核实现
/*
* Use local definitions of C library macros and functions
* NOTE: The function implementations may not be as efficient
* as an inline or assembly code implementation provided by a
* native C library.
*/
#ifndef va_arg
#ifndef _VALIST
#define _VALIST
typedef char *va_list;
#endif /* _VALIST *//*
* Storage alignment properties
*/
#define _AUPBND (sizeof (acpi_native_int) - 1)
#define _ADNBND (sizeof (acpi_native_int) - 1)/*
* Variable argument list macro definitions
*/
#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap) (void) 0
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
#endif /* va_arg */
4 _vsnprintf
函数声明:
int _vsnprintf(char* str, size_t size, const char* format, va_list ap);
参数说明:
char *str [out],把生成的格式化的字符串存放在这里.
size_t size [in], str可接受的最大字节数,防止产生数组越界.
const char *format [in], 指定输出格式的字符串,它决定了你需要提供的可变参数的类型、个数和顺序。
va_list ap [in], va_list变量. va:variable-argument:可变参数
函数功能:将可变参数格式化输出到一个字符数组。
用法类似于vsprintf,不过加了size的限制,防止了内存溢出(size为str所指的存储空间的大小)。