casio1374633

导航

 

都会用printf,但是printf本身是怎么做到的?昨天在看一个单片机打印变参类似printf函数实现的代码,觉得很有必要学习下.

参考的文章如下:

http://tieba.baidu.com/f?kz=329987122

http://topic.csdn.net/t/20030803/13/2103389.html

============================================================================================

C语言的函数是从右向左压入堆栈的,调用va_start后,
按定义的宏运算,_ADDRESSOF得到v所在的地址,然后这个
地址加上v的大小,则使ap指向第一个可变参数如图:

栈底 高地址
| .......
| 函数返回地址
| .......
| 函数最后一个参数
| ....
| 函数第一个可变参数 <--va_start后ap指向
| 函数最后一个固定参数
| 函数第一个固定参数
栈顶 低地址


然后,用va_arg()取得类型t的可变参数值, 先是让ap指向下一个参数:
ap += _INTSIZEOF(t),然后在减去_INTSIZEOF(t),使得表达式结果为
ap之前的值,即当前需要得到的参数的地址,强制转换成指向此参数的
类型的指针,然后用*取值
最后,用va_end(ap),给ap初始化,保持健壮性。

============================================================================================

printp("\n Minute elapsed: %03ds",LedCnt);

============================================================================================

printp是一个自定义的打印变参的函数.进去看了下:

里面有几个关键步骤再此记录>

1>void printp( char* ctrl, ...)

这个和标准C的printf的函数原型差不多, 后面的...表示有多个参数输入,而第一个ctrl指向字符串的首地址.

2> va_start(ap,v) ((ap) = (char *)&v + sizeof(v))

目的是使ap指向

3>va_list argp;

/* va_list: defines the type of the pointer which will point each unnamed argument. */

定义了一个专门用来指向参数类型的指针.

4>va_arg(argp, unsigned long)

函数原型:va_arg(ap, type) (((type *)((ap) += __va_sizeof__(type)))[-1])

/*
*va_arg(argp, unsigned long) 表示va_arg 返回argp的当前参数的值,因为本例中,argp指向第一个可变参数
*的首地址, 则返回的是LedCnt的数值, LedCnt的数值是unchar类型,强制类型转换为 unsigned long类型,
* 此类型在va_arg中与type参数匹配.

*/

5>va_end( argp);

表示清空参数列表,并置参数指针argp无效(指向NULL).

说明:指针arg_ptr被置无效后,可以通过调用 va_start()、va_copy()恢复arg_ptr。每次调用va_start() / va_copy()后,必须得有相应的va_end()与之匹配。参数指针可以在参数列表中随意地来回移动,但必须在va_start() … va_end()之内。

posted on 2013-04-29 20:51  casio1374633  阅读(384)  评论(0)    收藏  举报