printf 可变参数封装

#define fb (fmt, …) fa(fmt, ##__VA_ARGS__)     //##__VA_ARGS__就只是告诉编译器,fa可以接受可变参数,不要报错,相当于又把…传递下去了(两个#的解释请参见宋宝华《linux设备驱动程序开发详解 4.0内核》p80)
Void fa(fmt, …)
{
    xxxxx
}
typedef char *va_list;   //va_list就是个普通的指向char类型的指针而已
#define _INTSIZEOF(n)   (  (sizeof(n)+sizeof(int)-1)& ~(sizeof(int)-1)  ) //这是获取某个类型的长度,并按照int长度为边界对齐,不足4bytes补足,因为栈宽度为4bytes
#define va_start(ap,v)  (ap=(va_list)&v + _INTSIZEOF(v)) //这就是使ap指向v的以int长度为边界的后一个位置
#define va_arg(ap, type)  ( *(type *)((ap+=_INTSIZEOF(type) )-_INTSIZEOF(type))   )  //这其实就是,先使 ap+=_INTSIZEOF(type),跳过下一个4字节对齐了的type长度(改变了ap本身的值),然后再返回 ap-_INTSIZEOF(type)位置处的值,是个type类型
#define va_end(ap)  (ap=(va_list)0)
void printf(char *fmt, … )    //参数会按照从右向左的顺序依次入栈
{
    va_list va;
    va_start(va, fmt);   //根据fmt在栈上的地址,获取到 … 那一串参数的开始地址,即validate va的值
    va_arg(va);          //以后每调用一次va_arg(va),就获取了一个…的值,至于获取多少个是个头,fmt含有…参数个数的信息(要么直接给出,要么隐含有,比如字符串里%的个数)

    va_end(va);
}

printf 可变参数封装_第1张图片
约定:如果函数参数是带有…的,那就va_start一套组合拳获取参数值。如果传进来的是一个va_list的值,就省略va_start操作,因为va_start 就是validate va的值,人家都给你传进来了,你还多此一举干嘛。经过分析,一个…函数没有办法再次调用…函数,因为没有办法通过va_start正确找到 … 的开始位置!
除非使用宏,如下:

void fa(char *fmt, …)
{
    va_list va;
    va_start(va,fmt);
    //va_arg(va);
    //fb(fmt,va)

    va_end(va);
}

void fb(char *fmt, …)
{
    va_list v;
    va_start(v,fmt);
    //va_arg(v);

    va_end(v);
}

int main()
{
    int i=10;
    fa("hello fang,%d%s\n",i,"ding");
}

问题:一个含有…(可变参数)的函数,可不可以将…对应的参数全部传递给一个新的含…(可变参数)的函数?(即一个…函数可以调用另一个…函数吗?)

printf 可变参数封装_第2张图片

按照…的约定,fb()内部取参数的时候还是使用va_start(va,fmt)来初始化va(而不是使用传进来的va初始化它。fmt和va确实都指向了有效的地方,但是fb其实是想根据fmt这个指针所占内存的地址、即栈上的位置来拿参数的),所以第二次调用va_arg后,其实拿到的东西就不属于本函数栈里的内容了,即越界了!
但是Joey说可以,将参数格式化输出到buffer再将buffer传递下去。vsprintf()

你可能感兴趣的:(linux日常)