首先了解一个新知识,函数的参数列表的地址是顺序排列的,以下代码验证这一知识。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <stdio.h>
void func1(int a, int b, int c) { printf("%d\n",a); printf("%d\n",*(&a+1)); printf("%d\n",*(&a+2)); }
void func2(int a, int b, int c) { printf("%d\n",a); printf("%d\n",*((char*)&a+sizeof(a))); printf("%d\n",*((char*)&a+sizeof(a)*2)); }
int main() { func1(1,4,6); func2(2,5,8); return 0; }
|
由此来看可变参 va_list,va_start,va_arg,va_end
,就可以更好的理解了。
在 stdarg.h 中,定义了这些宏
1 2 3 4 5 6 7 8 9
| typedef char *va_list;
#define __va_argsiz(t) (((sizeof(t)+sizeof(int)-1)/sizeof(int))*sizeof(int))
#define va_start(ap, pN) ((ap)=((va_list)(&pN)+__va_argsiz(pN)))
#define va_end(ap) ((void)(ap=(va+list)0))
#define va_arg(ap, t) (((ap)=(ap)+__va_argsiz(t)),*((t*)(void*)((ap)-__va_argsiz(t))))
|
转换下来,与示例程序类似,区别是取得变长参数地址不同,因为要进行字节对齐,在32位或64位机器上,sizeof(int) 都是 4:
$$
__va_argsiz(t) <=> (\frac{sizeof(t)+sizeof(int)-1}{sizeof(int)}*sizeof(int))
$$
$$
va_start(ap, pN)<=> (ap=(char*)&pN+\frac{sizeof(pN)+sizeof(int)-1}{sizeof(int)}*sizeof(int))
$$
使用的例子:
1 2 3 4 5 6 7 8 9 10
| VOID iot_debug_print(CHAR *fmt, ...) { char buff[2048] = {0}; va_list args; va_start(args, fmt); vsnprintf(buff, 2048, fmt, args); iot_uart_write(OPENAT_UART_USB, (UINT8*)buff, strlen(buff)); va_end(args); }
|