stdarg.h编写可变参数函数

一.起因
     起因是自己想写一个简单的打印日志的函数,最大的问题是,每次打印的时候输入数据的个数是不同的。最开始的想法是用宏,这样就不管输入参数多少了,但是用宏的话就没办法判断输入参数的类型了。所以就想使用可变参数来实现,那么问题来了,我首先得学学怎么写可变参数函数。呐,stdarg.h可以帮助我。

二.探索
     编写可变参数的关键是,va_start() ,va_arg(), va_end()这三个函数(宏),和va_list这个类型,他们都在stdarg.h中定义。
在centos下,使用 man 3 va_start 就可以看到这三个函数的具体介绍了。

  1. void va_start(va_list ap, last)    这个函数初始化va_list结构体ap,改结构体在后续的 va_arg()和va_end()中调用。第二个参数是你编写的可变参数函数中最后一个已知类型的参数。
  2. type va_arg(va_list ap,type)  返回下一个可变参数。如果该函数是va_start()后第一次调用,则理所当然返回的是第一个可变参数。异常情况,如果没有下一个参数了或者,指定的type不匹配实际参数类型以及实际参数类型进行类型提升 后的类型,则返回随机错误。也因此需要特别的方法判断是否还有下一个参数
  3. void va_end(va_list ap) 反初始化结构体ap,与va_start()成对出现。注意在某些特殊的系统下,va_start() va_end()由宏实现,而且分别包含一个 { 和一个 },在这种情况下必须将这两个调用放在同一个函数中。我们在任何情况下,也最好尽量这样做。
三.实例
说了半天,改回到正题了。如何声明一个包含可变参数的函数呢?给个简单的例子,
void foo(char *fmt, ...);
从上面对va_start()函数的分析中,我们可以看出,至少必须提供一个指定类型的参数,在这里我们就是指定了fmt。

那么如何去解析这些可变参数呢,还是来看manual中的例子:
编写了一个foo函数,能够根据 fmt中的格式,将参数打印出来。这里的fmt就像是printf的第一个参数一样。

四.异常情况解决办法
     在前面介绍va_arg函数中就提到,当不存在下一个参数的时候,会返回随机错误。这点感觉挺不友好的,要是能返回个NULL的啥的,也好判断嘛。okay,接下来的工作就是要解决这个可能存在的异常情况,我们要精确的控制循环中va_arg调用的次数。上面的例子,是通过fmt字符串(改字符串由多个's' 'd' 'c'字符组成)中,'s' 'd' 'c'的个数来控制va_arg调用的次数的。或者我们可以更加直接点,直接指定一个整形参数n,比如下面的例子:

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

int mysum(int n, int * sum, ...);

int main()
{
    int err = 0;
    int sum = 0;
    do {
        err = mysum(3, &sum, 12, 234, 555);
        if ( 0 != err ) {  
            printf("function mysum exec failed!\n");
            break;
        }    

        printf("the sum is %d \n",sum);
    } while(0);

    return err;
}

int mysum(int n, int * sum, ...)
{
    int i = 0;
    int err = 0;
    int ival;
    va_list ap;  

    va_start(ap,sum);

    while( i < n ) {
        *sum += va_arg(ap, int);
        ++i;
    }

    va_end(ap);

    return err;
}

     这种指定n和linux manual中指定fmt的方式,本质上是一样的,都是已经将参数的个数n提供了,然后只需要调用循环n此就行了。我们前面提到,我们希望一种va_arg()返回NULL,从而判断循环结束的行为方式(就像是mysql中取row一样的方式)。默认是不提供的,但是我们可以认为的构造这样一种行为方式,那就是 默认最后一个传入的参数是NULL。类似execl 函数。

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

void print_strs(const char * first, ...);

int main()
{
    print_strs("RMS", "is", "a", "great", "persion", NULL);
     
    return 0;
}


void print_strs(const char * first, ...)
{
    char * sval;     
    va_list ap;  

    va_start(ap, first);

    /**  
     * 不要忘了第一个参数,0.0
     * */
    printf("%s ",first);
    while ( NULL != ( sval = va_arg(ap, char *)) ) {  
        printf("%s ", sval);
    }   

    va_end(ap);

    puts("");
    return ;
}

五.接下来要干什么?
     回到开头所说的,写一个简单的打印日志的函数呗!


                                                                                                                        by mzy

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值