C-可变参数个数函数的构建

printf(*format,…),其中的…就代表可变参数列表
需要包含的函数库

void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);

其中:
va_list是用于存放参数列表的数据结构。
va_start函数根据初始化last来初始化参数列表。
va_arg函数用于从参数列表中取出一个参数,参数类型由type指定。
va_copy函数用于复制参数列表。
va_end函数执行清理参数列表的工作。
注:va_start要与va_end配对使用;

上述函数通常用宏来实现,例如标准ANSI形式下,这些宏的定义是:

typedef char * va_list; 
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )

使用宏_INTSIZEOF是为了按照整数字节对齐指针,因为c调用协议下面,参数入栈都是整数字节(指针或者值)。
以上内容引用自C语言可变参数函数详解示例
关于宏_INTSIZEOF,我们不妨举个例子(以32位系统为例):

int a=8;
_INTSIZEOF(a)=((sizeof(a)+sizeof(int)-1)&~(sizeof(int)-1))
             =((sizeof(int)+sizeof(int)-1)&~(sizeof(int)-1))
             =(4+4-1)&~(4-1)
             =0x04&0xffff fffc
             =0x04

对于不同的类型如下:

_INTSIZEOF(char)=((sizeof(char)+sizeof(int)-1)&~(sizeof(int)-1))
                             =(2+4-1)&~(4-1)
                             =0x05&0xffff fffc
                             =0x04
_INTSIZEOF(float)=((sizeof(float)+sizeof(int)-1)&~(sizeof(int)-1))
                             =(4+4-1)&~(4-1)
                             =0x04&0xffff fffc
                             =0x04
_INTSIZEOF(double)=((sizeof(double)+sizeof(int)-1)&~(sizeof(int)-1))
                  =(8+4-1)&~(4-1)
                  =0x0b&0xffff fffc
                  =0x08

用上可看出,不足整数字节的参数类型将会被转化成整数字节(char型转化为int型(低精度转高精度,数据不会丢失),flaot型转换为int(高精度转低精度,数据丢失)),具体问题查看如下:

可变参类型陷阱

同类型参数列表用法如下:

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

#define MAX_ARGS 4

void print0(void){
    printf("print0\n");
}

void print1(int a){
    printf("print1 %d\n",a);
}

void print2(int a, int b){
    printf("print2 %d %d\n",a,b);
}
void print3(int a, int b, int c){
    printf("print3 %d %d %d\n",a,b,c);
}

void print4(int a, int b,int c,int d){
    printf("print4 %d %d %d %d\n", a, b, c, d);
}

void print(int num,...){
    int i = 0;
    int result = 0;

    int args[MAX_ARGS] = { 0 };

    int size = num > MAX_ARGS ? MAX_ARGS : num;

    va_list v;
    va_start(v, num);

    for (i = 0; i < size; i++){
        args[i] = va_arg(v, int);
    }
    switch (size){
    case 1:
        print1(args[0]);
        break;
    case 2:
        print2(args[0], args[1]);
        break;
    case 3:
        print3(args[0], args[1], args[2]);
        break;
    case 4:
        print4(args[0], args[1], args[2],args[3]);
        break;
    default:
        print0();
        break;
    }

    va_end(v);
}
void main(){
    print(0);
    print(1,1);
    print(2, 1, 2);
    print(3, 1, 2, 3);
    print(4, 1, 2, 3, 4);

    system("pause");
}

运行结果:
这里写图片描述

同类型的参数列表,起始就是采用指针对数组的操作类似,参数列表的内存分布如下:

num(offset:4Bytes)
arg1(offset:4Bytes)
arg2(offset:4Bytes)
arg3(offset:4Bytes)
arg4(offset:4Bytes)

则取参数列表的步骤如下:

va_list v;
//step1:pa指针指向num表,并跳过num头
va_start(v,num);
//step2:提取argi参数
for(int i=0;i<num;i++){
args[i]=va_arg(v,int);
}
//step3:根据输入参数个数,分别执行不同的函数
switch(num){
case 1:xxx1(args[0]);break;
case 2:xxx2(args[0],args[1]);break;
case 3:xxx3(args[0],args[1],args[2]);break;
...
}
//step4:清空va_list指针
va_end(v);

当然了,以上只是一般形式,可参考printf(format*,…)形式,传入字符串,完成不同类型参数的传递;

不同类型参数列表用法如下:

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

void print1(char* a){
    printf("print1 %s\n",a);
}

void print2(char* a, float b){
    printf("print2 %s %f\n",a,b);
}

void print(int num,...){
    int i = 0;
    int result = 0;

    char* name = NULL;
    double arg2 ;

    va_list v;
    va_start(v, num);

    if (num == 1){
        name = va_arg(v, char*);
        print1(name);
    }
    else{
        name = va_arg(v, char*);
        arg2 = va_arg(v, double);//关于arg2参数必须为double,否则程序出错
        print2(name, arg2);
    }

    va_end(v);
}
void main(){
    print(1, "Bob");
    print(2, "Lily", 13.02);

    system("pause");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值