-
先看一下printf函数的原型.
char print_buf[BUF_SIZE]; void printf(const char *fmt, ...) { va_list ap; //定义一个指针变量 unsigned int i; va_start(ap, fmt); i = vsprintf(print_buf, sizeof(print_buf), fmt, ap); va_end(args); __put_char(print_buf, i); }
printf(const char *fmt, …)是一个可变参数函数, 第一个参数为字符串, 后面是格式化输出参数列表. C语言中函数的参数都是压进栈中的(参数压栈方向是从右往左!), 可变参数的函数必须有一个参数表示参数的个数, 才能让编译器知道压栈多少参数, 以及函数返回时弹出多少个参数, 我们在fmt字符串中提取’%‘的个数, 以及针对’%'后面不同的字符来处理.
-
va_list类型
va_list 的 va 是variable-argument(可变参数). list 就是列表的意思
typedef char *va_list; //Linux内核中va_list的实现 va_list ap;
-
va_start
va_start(ap, A)宏将ap初始化,以便随后被va_arg()和va_end()使用,必须先调用。
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND)))) // Linux 内核中宏定义原型 #define va_start(ap,fmt) ( ap = (va_list)&fmt + _INTSIZEOF(fmt) )
第一个参数ap, 就是刚刚提到的va_list类型, 第二个参数A, 就是一个确定的类型, 也就是"…"的类型, 他会将这些类型的地址放到va_list中, 根据初始化A来初始化AP.
注意: 传给宏va_start 的参数fmt是可变参数列表中的前一个参数, ap指向变参列表中的第一个参数地址.
注意: 函数参数压栈时, 参数的入栈顺序时从右往左, 出栈时是从左往右. 函数调用时, 先把若干个参数都压入栈中, 再压fmt, 最后压pc, 这样一来, 栈顶指针偏移找到了fmt, 通过fmt中的%占位符, 取后面参数的个数, 从而正确获得所有参数.
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
计算int类型按4字节对齐后的结果. 通过_INTSIZEOF(n), 可以根据一个变量的类型计算变量再内存中占用的字节数, 从而正确定位参数在内存的位置.
-
va_arg
va_arg(ap, T)宏扩展为一个表达式, 该表达式具有调用中下一个参数的类型和值.
va_arg()宏在va_start()宏之后的第一次使用会返回最后的参数. 连续的调用会返回其余参数的值.
#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADN BND))))// Linux 内核中宏定义原型 #define va_arg(ap,type) ( *(type *)((ap += _INTSIZEOF(type)) - _INTSIZEOF(type)) )
ap 是一个va_list类型, T是一个类型. 从参数列表中取出一个参数, 类型就是T.
-
va_end
va_end 是一个宏
#define va_end(ap) (ap = (va_list) NULL) #define va_end(ap) ( ap = (va_list)0 )
通过这个宏, 可以清楚的看到, 将va_list类型的ap赋值为NULL.
-
__put_char() 将字符逐个打印到串口输出寄存器中.
void __put_char(char *p, int num) { while(*p && num--) { *(volatile unsigned int *)0xd00000020 = *p++; }; }
-
vsprintf(char *buf, const char *fmt, va_list args) 函数, 将变量列表args中的参数按照fmt中规定的格式保存到临时缓冲buf中.
int vsprintf(char *buf, canst char *fmt, va_list args) { unsigned NUM_TYPE num; int base; char*str; int flags; int field_width; int Precision; int qualifier; str = bUf; for (; *fmt ; ++fmt) { if (* fmt ! = ' % ' ) { *str++ = *fmt; continue; } /* process flags */ flags = 0; repeat: ++fmt;/* skip first "%" */ switch(*fmt) { case '- ' : flags |= LEFT;goto repeat; case '+ ' : flags |= PLUS;goto repeat; ... } ... base = 10; switch (*fmt){ case 'c': ... *str++ (unsigned char)va_arg(args,int); ... continue; case 's': str = string(str,va_arg(args, char *),field_width,precision,flags); continue; ... case ' X ' : base = 16; break; case 'd': case ' i ' flags |= SIGN; case 'u': break; default: * str++ ='%'; if (*fmt) *str++ = *fmt; else --fmt ; continue; } str = number (str, num, base, field_width, precision, flags) ; } *str == '\0'; return str-buf; }
-
Linux 中 printf 函数不定长参数的实现
最新推荐文章于 2023-08-28 22:59:43 发布