Pile 0006: 函数可变参数-va_list、va_start、va_arg、va_end

  由于在C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦;即使采用C++,如果参数个数不能确定,也很难采用函数重载.对这种情况,我们可以采用指针参数来解决问题。读取可变参数的过程其实就是在堆栈中,使用指针,遍历堆栈段中的参数列表,从低地址到高地址一个一个地把参数内容读出来的过程。

  va_list/va_start/va_arg/va_end这几个宏,都是用于函数的可变参数的。

 源码

///stdarg.h
#define va_start _crt_va_start
#define va_arg _crt_va_arg
#define va_end _crt_va_end

///vadefs.h
#define _ADDRESSOF(v)     ( &reinterpret_cast<const char &>(v) )
#define _INTSIZEOF(n)     ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
typedef char *  va_list;

#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 )

    va_list用于声明一个变量,我们知道函数的可变参数列表其实就是一个字符串,所以va_list才被声明为字符型指针,这个类型用于声明一个指向参数列表的字符型指针变量,例如:va_list ap;//ap:arguement pointer
    va_start(ap,v),它的第一个参数是指向可变参数字符串的变量,第二个参数是可变参数函数的第一个参数,通常用于指定可变参数列表中参数的个数。
    va_arg(ap,t),它的第一个参数指向可变参数字符串的变量,第二个参数是可变参数的类型。
    va_end(ap),用于将存放可变参数字符串的变量清空(赋值为NULL).

    _INTSIZEOF 宏,获取类型占用的空间长度,最小占用长度为int的整数倍;以int所占的字节为标准进行对其操作,如果int占四字节,则以四字节对齐为标准读取数据。

分析:

  #define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

    首先是ap = ap + __INTSIZEOF(t)。此时ap已经被改变了,它已经指向了下一个参数,我们令x=ap + __INTSIZEOF(t);那么括号内就变成了(x – __INTSIZEOF(t)),但是这里没有赋值运算符,所以ap的值没有发生变化,此时ap仍然指向的是当前参数的下一个参数的位置,也就是说ap指向的位置比当前正在处理的位置超前了一个位置。
    其实写成下面的形式就简单明了了:
    #define   va_arg(ap,t)   (*(t   *)((ap   +=   _INTSIZEOF(t)),   ap   -   _INTSIZEOF(t))   )
    va_start(ap,v)已经将ap指向了可变参数列表的第一个参数了,以后我们每一步操作都需要将ap移动到下一个参数的位置,由于我们每次使用可变参数的顺序是:va_start(ap,v)—>va_arg(ap,t);这样我们在第一次去参数的时候,其实ap已经指向了第二个参数开始的位置,所以我们用表达式的方式获得一个指向第一个参数的临时指针,这样我们就可以采用这种一致的方式来处理可变参数列表。

以Demo 0001为例,vaptr指针所指向的参数如下图所示:

每次va_arg操作后,vaptr指针都指向下个参数,但是va_arg宏的值是当前参数的值,最后vaptr必须通过va_end清零。

Demo 0001

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>

int sum(int number,...)
{
    va_list vaptr;
    int i;
    int sum = 0;

    va_start(vaptr,number);
    for(i=0; i<number;i++){
        sum += va_arg(vaptr,int);
    }
    va_end(vaptr);

    return sum;
}

int main()
{
    printf("%d\n",sum(4,4,3,2,1));
    system("pause");
    return 0;
} 

 

 printf重定义

void Console_Printf(char *format, ...)
{
    static char buffer[100 + 1];
    va_list vArgs;
    
    va_start(vArgs, format);
    vsprintf((char *)buffer, (char const *)format, vArgs);
    va_end(vArgs);

    if (pConChan) {
        Serial_WriteStr(pConChan, (char *)buffer);
    }
}

 

posted @ 2016-11-01 10:27  窝窝头HZ  阅读(309)  评论(0编辑  收藏  举报