有关变长参数,和printf那俩基佬的事儿

 1 int printf(const char *format, ...)
2 {
3 va_list arg;
4 int done;
5 va_start (arg, format);
6 done = vfprintf (stdout, format, arg);
7 va_end (arg);
8
9 return done;
10 }

初学C语言,我一直搞不懂它是怎么获得参数的数据类型的,我只能猜测是由占位符所决定的

1.printf传递已经初始化好了的arg的地址给vfprintf

2.vfprintf处理第一个format,当获得一个占位符的时候,就将arg的指针后移该占位符所代表的数据类型所占的空间的大小,直到把format遍历完为止。

因为看c primer plus,他是必须要提交一个参数个数才可以使用变长参数。

一个确实的证据就是 printf("%d,%d,%d",1); 的输出结果

似乎变长参数在处理不是像printf这样的格式化类的函数上用途不是很广?才疏学浅不敢定论

下面是对va_start那一类宏的理解,我们转到定义

#ifndef _VA_LIST_DEFINED
#ifdef _M_CEE_PURE
typedef System::ArgIterator va_list;
#else
typedef char * va_list;
#endif /* _M_CEE_PURE */
#define _VA_LIST_DEFINED
#endif
#define va_start _crt_va_start
#define va_arg _crt_va_arg
#define va_end _crt_va_end
#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap) ( ap = (va_list)0 )
#ifdef  __cplusplus
#define _ADDRESSOF(v) ( &reinterpret_cast<const char &>(v) )
#else
#define _ADDRESSOF(v) ( &(v) )
#endif

1.va_list是一个char*
2.还没学Cpp,_ADDRESSOF就是 &v,即变长参数前的那个变量(这也就能说明为什么变长参数前必须要有非变长变量,找入口)
3. va_start的用途就是把指针指到变长参数的起始位置(最后一个非变长参数的末尾)
4.va_arg:

*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))  ap变了,但是这个表达式返回的却是没变前的值——既改变了ap,使他指向下一个参数,又使得返回值是当前参数的首地址。
5.va_end  将指针重置。

 

至于_INTSIZEOF实在不能理解,刚刚看了一下 http://www.cnblogs.com/stli/archive/2010/11/09/1873152.html

引用

1#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
我们这里简化一下这个宏:
#define _INTSIZEOF(n) ((sizeof(n) + x) & ~(x))
x = sizeof(int) - 1 = 3 = 0000 0000 0000 0011(b)
~x = 1111 1111 1111 1100(b)

当一个数 & (-x)时,得到的值始终是sizeof(int)的倍数,也就是说_INTSIZEOF(n)的功能是将n圆整到sizeof(int)的倍数上去。sizeof(n) >= 1, sizeof(n)+sizeof(int)-1经过圆整后,一定会是>=4的整数;在其他系统平台上,圆整的目标值有的是4,有的则是8,视具体系统而定。

参考C traps and falls 然后写的一个版本:

#define va_start(ap,v) ((ap) = (char *)&(v)) //将ap初始化为变长参数的头部指针
#define va_end(list) ((list)=(char *)0) //置0
#define va_arg(list,mode) ((mode *)((list) += sizeof(mode)))[-1] //将指针移动到下一个参数,并且返回左移的值[-1](数组下标表示偏移量)

C traps and falls:mode一定不能为char short 和 float 型的参数,char/short会自动转换成int  float会自动转换成double,作为参数时,指针不会被转换,只有char float short类型的数值才会被转换

posted @ 2012-02-07 10:46  latyas  阅读(464)  评论(0编辑  收藏  举报