返回类型 函数名(已知类型,个数的参数列表,Type parmN,...);
parmN是最右边的已知类型的参数名;如:
void f(int num,...) /** parmN就是"num" */
/** * 初始化ap,必须先于va_arg(),va_end(),va_copy被调用. * @param ap va_list类型变量. * @param parmN 见上 * @warning 由于需要parmN的地址来初始化ap,所以表达式'&(parmN)'应该是合法的,即 * parmN不可以是寄存器变量,数组,函数. * */ void va_start(ap,parmN)
/** * 返回下一个参数的值. * va_arg()宏展开后是一个表达式,该表达式的结果就是下一个参数的值.每一次调用都会更改ap的状态,从而完成遍历参数列表的工作 * @param ap va_start()初始过的va_list变量 * @param type 下一个参数的类型 * @warning 指向type类型变量的指针类型应该是'type *';如type可取int,double...但是type不可以是数组类型,函数类型 * @warning 受'默认实际参数提升'规则影响,type的值不可能是: * char,unsigned char,signed char(如果期望参数类型为这些,则type是int) * short,unsigned short,unsigned short,short int,unsigend short int,signed short int(如果期望参数类型为这些,则type是int) * float(如果期望参数类型为这个,则type是double) * */ type va_arg(ap,type)
在调用一个不带原型声明的函数之前,编译器会对每个实参执行"默认实际参数提升"后在传递给函数。具体规则如下
char、short和相应的signed、unsigned类型的实际参数提升到int,如果int不能存储原值,则提升到unsigned int
float类型的实际参数将提升到double
同样在调用具有可变参数列表的函数时,也会运用'参数提升规则',所以在函数内部是不会出现char,short,float类型的参数的!所以Type的值不可能是...
/** 在结束对可变参数列表的遍历后调用 */ va_end(ap)
在调用va_start()对ap进行初始化之后,可以在函数中或者她调用的其他函数中执行va_arg()遍历可变参数
/** * 遍历并打印可变参数列表. * @param num 可变参数列表中参数个数 * @param ap 由调用者传递,ap中存放若干个int类型的参数 */ void printArgs(int num,va_list ap){ printf("Begin...\n"); while(--num >= 0) printf("%d\t",va_arg(ap,int)); printf("\nOver...\n"); return ; } void f1(int num,...){ va_list ap; va_start(ap,num); printArgs(num,ap); // printArgs(num,ap);/* 可以验证ap的状态被改变了 */ va_end(ap); return ; }
如上在printArgs()中由于使用了va_arg所以会更改ap的状态,那么在f1调用printArgs()之后f1中的ap的状态是否会变化?
根据对va_list实现的不同,ap可能采用传值或者传址调用,即无法保障f1中ap一定不变
如上函数,运行后f1中ap的状态被改变了
/** * copies the (previously initialized) variable argument list src to dest. * 行为就像: * va_start(des,parmN); * [va_start() were applied to dest with the same last argument] * 若干次的va_arg(des,type)调用 * [followed by the same number of va_arg() invocations that was used to reach the current state of src.] * */ void va_copy(va_list des,va_list src);
void printArgs(int num,va_list ap){ va_list xap; va_copy(xap,ap); /* 执行一次copy操作 */ printf("Begin...\n"); while(--num >= 0) printf("%d\t",va_arg(xap,int));/* 使用xap遍历参数列表,不会改变ap的状态 */ printf("\nOver...\n"); va_end(xap); return ; } void f1(int num,...){ va_list ap; va_start(ap,num); printArgs(num,ap); printArgs(num,ap);/* 可以验证ap的状态不会被改变 */ va_end(ap); return ; }
void printArgs(int num,va_list *ap){ /* 传递ap的地址 */ printf("Begin...\n"); while(--num >= 0) printf("%d\t",va_arg(*ap,int)); /* 解引用 */ printf("\nOver...\n"); va_end(*ap); return ; } void f1(int num,...){ va_list ap; va_start(ap,num); printArgs(num,&ap); printArgs(num,&ap); va_end(ap); return ; }