stdarg.h

来源:互联网 发布:apache spark python 编辑:程序博客网 时间:2024/05/16 16:04

现在我们要说C语言一个强大的功能,它允许程序员自己定义可接受一个可变参数列表的函数。那么要咱们自己编写一个可接受一个可变参数列表的函数,就得用到头文件starg.h。这些宏允许在任何时候从头到尾地遍历一个附加的参数列表。注意,在遇到每一个参数之前,必须知道它的类型。不过在一个给定的调用发生之前,不必知道细节。可以使用一个固定的参数,例如一个格式串,来确定参数的数目和类型(参考stdio.h中的库函数scanf和printf)。
注意,一个我们自己定义的可变参数列表函数必须至少声明一个固定的参数,以便va_start对可变参数列表进行定位;一个函数在返回到它的调用者之前一定要调用va_end,因为一些实现在函数返回之前需要整理控制信息。
头文件stdarg.h中声明了一种类型而且定义了3个宏。《C标准库》中的va_start和va_arg将作为宏实现,而不是实际的函数。

/** *stdarg.h standard header */#ifndef _STDARG /*保护宏*/    #define _STDARG    #define _AUPBND 1    #define _ADNBND 1 /*宏AUPBND、ADNBND在yyvals.h中都是有的,这里简单设置为1*/    typedef char *va_list;    #define va_arg(ap, T) \        (*(T *)(((ap) += _Bnd(T, _AUPBND)) - _Bnd(T, _ADNBND)))    #define va_end(ap) (void)0    #define va_start(ap, A) \        (void)((ap) = (char *)&(A) + _Bnd(A, _AUPBND))    #define _Bnd(X, bnd) (sizeof(X) + (bnd) & ~(bnd))#endif

宏va_start:

#include <stdarg.h>void va_start(va_list ap, parmN);

在我们编写的可变参数列表函数中,一定要在访问所有未命名的参数之前调用宏va_start。宏va_start对ap进行初始化,以便后面va_arg和va_end对它的使用。parmN是函数定义(在…之前的那个参数)的可变参数列表中最右边参数的标识符。
宏va_arg:

#include <stdarg.h>type va_arg(va_list ap, type);

参数类型type是指定的类型名字,使得指向指定类型的数据对象的指针的类型可以简单地通过在类型后面加一个后缀*来获得。使用va_start初始化ap之后,对va_arg的第一次调用返回的是parmN指定的参数后面的参数值。后面的调用一次返回剩下的参数值。
宏va_end:

#include <stdarg.h>void va_end(va_list ap);

宏va_end促使我们编写的函数的正常返回,该函数的可变参数列表被初始化了va_list ap的va_start的扩展引用。宏va_end可能会对ap进行修改,这样ap就不在有用(类似参考fopen和fclose)。如果我们没有使用va_start或者再返回之前没有编写va_end,可能会出现一些问题。
关于stdarg.h的使用,有一些限制:首先我们必须明确地声明一个函数具有一个可变参数列表(参考scanf和fprintf)。声明函数时必须至少有一个固定的参数,最后一个固定参数引用时一般称为parmN。必须声明一个va_list类型的数据对象,一般称为ap。必须在函数中执行va_start(ap, parmN)之后才能执行va_arg和va_end。如果前面执行了va_start,必须在函数内执行va_end。一旦执行了va_end,就不能再执行va_arg。除非再次执行va_start重新初始化ap。也就是说又一次va_start就必须对应一次va_end(还是类似参考fopen和fclose)。
现在来看,咱们自己编写的一个接受可变参数列表的函数:

/** *对<stdio.h>中声明的函数fputs的推广,这个函数va_fputs可以将任意数目的串 *写到指定的流中。 */#include <stdarg.h>#include <stdio.h>int va_fputs(FILE *fp, ...);/*fputs的推广*/int main(){    char *s1 = "hello ";    char *s2 = "world";    va_fputs(stdout, s1, s2, NULL);/*不要忘记最后一个参数写NULL*/    return 0;}int va_fputs(FILE *fp, ...){    char *s;    int status = 0;    va_list ap;    va_start(ap, fp);/*初始化ap*/    while (NULL != (s = va_arg(ap, char *)))/*循环使用参数*/        if (fputs(s, fp) < 0)            status = EOF;    va_end(ap);    /*执行了va_start,那么必须执行va_end*/    return (status);}

更复杂的可变参数列表函数请参考scanf和printf等库函数,非常地有意思。

0 0
原创粉丝点击