可变参数函数

函数声明:

  • 返回类型 函数名(已知类型,个数的参数列表,Type parmN,...);

  • parmN是最右边的已知类型的参数名;如:

void f(int num,...)
/** parmN就是"num" */




va_start

/**
 * 初始化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

/**
 * 返回下一个参数的值.
 * 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


/** 在结束对可变参数列表的遍历后调用 */
va_end(ap)

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的状态被改变了

确保不被共享

va_copy


/**
 * 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 ;
}







你可能感兴趣的:(va_start,va_arg,va_copy,va_end,va_list)