写在前面: (转载文章,若有不妥,通知后我会立即删除)
一、我们先来看几个宏:va_list、va_start、va_arg及va_end(va的意思应该是variable),在Linux-2.6.24.7内核源码里,其定义(内核里的定义与C语言库的定义是类似的)如下
/* * 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 _VALISTtypedef 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 */
1、va_list
va_list表示可变参数列表类型,实际上就是一个char指针
2、va_start
va_start用于获取函数参数列表中可变参数的首指针(获取函数可变参数列表)
* 输出参数ap(类型为va_list): 用于保存函数参数列表中可变参数的首指针(即,可变参数列表)
* 输入参数A: 为函数参数列表中最后一个固定参数
3、va_arg
va_arg用于获取当前ap所指的可变参数并将并将ap指针移向下一可变参数
* 输入参数ap(类型为va_list): 可变参数列表,指向当前正要处理的可变参数
* 输入参数T: 正要处理的可变参数的类型
* 返回值: 当前可变参数的值
在C/C++中,默认调用方式_cdecl是由调用者管理参数入栈操作,且入栈顺序为从右至左,入栈方向为从高地址到低地址。因此,第1个到第n个参数被放在地址递增的堆栈里。所以,函数参数列表中最后一个固定参数的地址加上第一个可变参数对其的偏移量就是函数的可变参数列表了(va_start的实现);当前可变参数的地址加上下一可变参数对其的偏移量的就是下一可变参数的地址了(va_arg的实现)。这里提到的偏移量并不一定等于参数所占的字节数,而是为参数所占的字节数再扩展为机器字长(acpi_native_int)倍数后所占的字节数(因为入栈操作针对的是一个机器字),这也就是为什么_bnd那么定义的原因。
4、va_end
va_end用于结束对可变参数的处理。实际上,va_end被定义为空.它只是为实现与va_start配对(实现代码对称和"代码自注释"功能)
对可变参数列表的处理过程一般为:
1、用va_list定义一个可变参数列表
2、用va_start获取函数可变参数列表
3、用va_arg循环处理可变参数列表中的各个可变参数
4、用va_end结束对可变参数列表的处理
二、int vsprintf(char *string, char *format, va_list param);
1、将参数按照格式输出到string中,主要用于串口通信中;
void UART1_Printf(char *fmt,...)
{
va_list ap;
char string[128];
va_start(ap, fmt);
vsprintf(string, fmt, ap);
UART1_SendString(string);
va_end(ap);
}