使用可变参数列表,可以让函数在不同场合接收不同数量的参数传入,printf函数的格式化输出,就是一个典型的例子。

printf("<格式化字符串>", <参量表>);            //printf函数的使用
int printf(const char *format, ...);        //printf函数的原型

 

函数对可变参数的访问

可变参数列表通过定义与starg.h的宏来实现。构造这种函数,利用一个类型va_list 和 三个宏va_start、va_arg、va_end来实现对可变参数的访问。下面的例程实现对传入数值求平均值,并返回平均值。

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

/*
**求均值函数
**输入:n_values 指定数量  <参数表>
**输出:求出的平均值
*/
float average( int n_values, ... )    // ...表示可变参数列表,此时这些参数的类型还没有确定
{
    va_list var_arg;    //用于遍历列表中的参数
    int count;
    float sum = 0;

    va_start( var_arg, n_values );

    for( count=0; count < n_values; count +=1 )
    {
        sum += va_arg( var_arg, int );        //为每下个可变参数指定 int 类型

    }
    va_end( var_arg );

    return sum/n_values;
}

void main(void)
{
    float temp = 0;

    temp = average( 3, 6, 3, 3 );    //3个数值分别是 6、3、3

    printf("%f\n",temp);
}


实现

 

分析

va_start根据第二个参数n_values(函数average的最后一个有名参数),将va_list类型变量var_arg指定列表中第1个参数

    va_start( var_arg, n_values );

va_arg根据第二个参数int来指定va_list类型变量var_arg在列表中的下一个参数

    va_arg( var_arg, int );

va_end表示访问完毕

    va_end( var_arg );


但是根据上面的结果,有两点不足

1.使用average函数需要指定传入参数的数量,不方便
2.没有办法判断每一个可变参数的类型

 

构造printf()--串口输出

想想在printf函数中,通过格式化输出(匹配格式号),我们不仅不用指定参数的数量,还不用指定参数的类型。
这里使用vsprintf函数结合上面的可变列表,来构造一个串口输出的实例

int vsprintf(char *str, const char *format, va_list arg);    //vsprintf函数原型

//串口2,printf 函数
//确保一次发送数据不超过USART2_MAX_SEND_LEN字节
void u2_printf(char* fmt,...)  
{  
    u8 *TXStr;
    va_list ap;
    va_start(ap,fmt);
    vsprintf((char*)USART2_TX_BUF,fmt,ap);
    va_end(ap);
    
    TXStr = (u8*)USART2_TX_BUF;
    
    while(*TXStr != 0)    //当前字符不为空    
    {
        USART_SendData(USART2, *TXStr);         //向串口1发送数据
        while(USART_GetFlagStatus(USART2,USART_FLAG_TC)!=SET);//等待串口2发送结束  TX自动置0
        TXStr++;
    }

}

同样,先声明va_list类型变量ap,va_start宏将ap指向可变列表第一个参数;vsprintf将可变列表格式化输出到内存,fmt代表格式化匹配规则;最后va_end结束。然后就可以通过串口底层,将USART2_TX_BUF中的数据发送到连接串口的另一端设备了。