1.va_list等
先了解一下调用函数时,实参入栈的方式
stdarg.h中的定义
#inclde<stdarg.h>
typedef char *va_list;
#define va_start(list,param1) ( list = (va_list)¶m1+ sizeof(param1) )
#define va_arg(list,mode) ( (mode *) ( list += sizeof(mode) ) )[-1]
#define va_end(list) ( list = (va_list)0 )
- va_list:本质上就是一个字符指针
- va_start:
使入参list指向第一个变参的地址。注意!!不是第一个参数,如上printf()的例子,a是第一个变参,而不是一开始的那个字符。以上面的例子解释:
(va_list)¶m1 = (char *)0
sizeof(param1) = sizeof(char *) = 4
list = 0 + 4 = 4
此时,list里存放的是变量a的地址
- va_arg:
实现:返回此时list指向的变参,并且让list指向下一个变参
按结合律来分析,首先执行的是
list += sizeof(mode)
意思就是list指向了下一个变量,因为加的是 本次指向的变量 的类型
(mode *) ( list += sizeof(mode) )
再把改变之后的值变为 指向刚刚的变量 的类型指针
后面有一个[-1],就代表返回的是 刚刚指向的变量
上面讲的太混乱,结合例子看
假设此时list = 4,即目前刚刚执行完 va_start(list,format) 这一句,一个变参还没有使用时
执行 int *a = va_arg(list,int); 因为我知道第一个变参是int型,所以这里才直接这么写,如果不知道变参类型是不能用的
list += sizeof(int); ==>list=8; 此时list就变成了8
(int)list == list(8) ; 把8变成了int型的地址,也就是说接下来对8自加或者自减,都是加减4
list[-1]; 这句就类似于数组了,数组中括号表示的是第几个元素,但是数组中没有负数
而如果是指针的话,就可以有负数,-1表示该种类型前一个地址,即地址减减
int 型地址8减减,就是4
所以最终,这个宏返回的是int型地址4,即第一个变参a的地址,而此时的list中存的已经是变参b的地址
va_end:
这个就是把第一开始定义的list变量,指向NULL,防止野指针
关于地址 后[-1]的介绍,可以参考一下以下博客
http://www.dengb.com/Cyy/1347567.html
printf家族及printf的实现
int vprintf / vscanf(const char * format, va_list ap); // 格式化format到标准输出
int vfprintf / vfsacanf(FILE * stream, const char * format, va_list ap); // 格式化format到stream文件
int vsprintf / vsscanf(char * s, const char * format, va_list ap); // 格式化format到字符串s
用以上三种方法实现printf(自己写的,还没测试)
//vprintf实现
int printf(const char * format, ...)
{
int iNum;
va_list list;
va_start(list,format);
iNum = vprintf(format , list);
va_end(list);
return iNum;
}
int printf(const char * format, ...)
{
int iNum;
va_list list;
va_start(list,format);
iNum = vfprintf(stdin,format , list); //stdin是FILE *; STDOUT_FILENO是整形的文件句柄
va_end(list);
return iNum;
}
int printf(const char * format, ...)
{
char buff[1000];
int iNum;
va_list list;
va_start(list,format);
iNum = vsprintf(buff ,format , list); //返回一共多少个字符
va_end(list);
buff[iNum] = '\0' //加上截至符
write(STDOUT_FILENO , buff , strlen(buff));
return iNum;
}