美文网首页C语言
可变参数列表

可变参数列表

作者: 风情云 | 来源:发表于2020-04-01 12:02 被阅读0次

在使用函数函数时,函数得形参并没有确定,这时就需要一个可变形参的出现。可变参数列表完全符合这个要求。其实,在一开始时就接触可变参数列表,就是printf函数

printf("%d %d %d\n",1,2,3);

printf函数内部得参数并没有确定,可变得的。接下介绍可变参数列表:

定义可变参数列表

//返回值类型 函数名 (数据类型 变量名,...)
int add(int count,...);

使用可变形参列表需要使用以下函数,头文件<stdarg.h>

可变形参列表
va_list 可变参数列表数据类型
type va_arg( va_list argptr, type );
void va_end( va_list argptr );
void va_start( va_list argptr, last_parm );
void va_copy(va_list dest, va_list src);
首先,必须调用va_start() 传递有效的参数列表va_list和函数强制的第一个参数。第一个参数代表将要传递的参数的个数。
其次,调用va_arg()传递参数列表va_list 和将被返回的参数的类型。va_arg()的返回值是当前的参数。
再次,对所有的参数重复调用va_arg()
最后,调用va_end()传递va_list对完成后的清除是必须的。
#include<stdio.h>
#include<stdlib.h>
#include<stdarg.h>
int add(int num,...)
{
    //可变参数类型
    va_list list;
    //获得形参的参数数据类型与参数个数
    va_start(list,num);
    int i = 0,sum = 0;
    for( ; i<num; i++)
    {
        //返回当前形参的数据
        sum += va_arg(list,int);
    }
    //形参列表结束
    va_end(list);
    return sum;
}
int main()
{
    int sum = add(5,1,2,3,4,5);
    printf("sum = %d\n",sum);
    return 0;
}

结果

sum = 15

在可变参数列表中,要注意强制要写的是参数个数,这个不能省略。调用函数时,注意参数要对应函数实现得数据类型,不然会得到错误得结果。参数个数也要对应形参个数,不然也会出错。

int sum = add(2,1.0,2);
printf("%d\n",sum);

结果

1072693248

为什么是错误的结果?想要得到正确的答案要如何修改?就要看看可变参数列表得原理

可变参数列表原理

int a = 0;
printf("%d %d %d \n",a++, ++a, a);

结果

1 2 2

为什么结果不是0 2 2呢?这个和函数形参的传参顺序有关,函数是从右往左传入参数。

形参传参顺序

void Test(int a, int b, int c)
{
    printf("a: %u\n",&a);
    printf("b: %u\n",&b);
    printf("c: %u\n",&c);
}

结果

a: 6356720
b: 6356724
c: 6356728

由结果可以看到,地址是递增的,我们也清楚,先入栈的地址比后入栈的地址高,验证函数形参是从右往左传参的。就可以明白上面的结果为什么是1 2 2。


过程

所以从栈顶到栈底的参数为第一个参数,第二个参数,第三个参数...,
上面的结果可以得出,我们可以通过得到第一个形参的地址,就可以得到后面形参的数据。

void Test(int a, int b, int c)
{
    int *ptr = &a;
    printf("a: %u\n",*ptr);
    printf("b: %u\n",*(ptr+1));
    printf("c: %u\n",*(ptr+2));
}
int main()
{
    int a = 0, b = 1, c = 2;
    Test(a, b, c);
    return 0;
}

结果

a: 0
b: 1
c: 2

通过上面的铺垫,可变参数列表也是和上面一样的原理:

//va_list原型 typedef char* va_list;
va_list list;  //定义一个指针
//将指针指向第二个形参(第一个形参为形参个数说明,不是实际要参与运算的形参)
//num为形参个数
va_start(list,num); 
//通过解引用获得指针指向地址的数据,且更新指针,将指针指向下一个形参
//int为下一个形参的数据类型,注意这个类型要与实际形参要一致,不然出错
va_arg(list,int);
//将指针置为空
va_end(list);

如果在va_arg函数中传入的数据类型与实际传入的数据类型不一致,那么会导致指针递增的指针步长不同,得到错误的数据。这就解释上文的答案为什么是错误的,正确的是如何的?

//与上文代码一致,仅修改这句
sum += va_arg(list,float);

float sum = add(2,1.0,2.0);
printf("%f\n",sum);

结果

在32位平台下,运行错误

这个是因为类型的自动提升,形参的类型会自动提升

形参类型自动提升

char *c;
printf("%c %d %ld %f %lf \n",c,c,c,c,c);

[图片上传失败...(image-1c5e78-1585713718399)]
可以看到类型会自动提升,形参只接受几种数据类型
char ---> int
float ---> double
float会自动提升为double,所以指针步长不一致,如果想要获得正确的结果,就要改成double数据类型。

sum += va_arg(list,double);

double sum = add(2,1.0,2.0);
printf("%lf\n",sum);

结果

3.000000

注意事项

va_arg函数在更新指针时,不能跳过某个形参不去读取,只能顺序读取。如果想要获得某个形参的地址可以使用va_copy函数,得到地址,再通过解引用得到数据。

#include<stdio.h>
#include<stdlib.h>
#include<stdarg.h>
int add(int num,...)
{
    va_list list,c_list;
    va_start(list,num);
    int i = 0;
    double sum = 0.0;
    for( ; i<num; i++)
    {
        //指针地址复制
        if(i == 0) va_copy(c_list,list);
        sum += va_arg(list,double);
    }
    va_end(list);
    //类型强转
    printf("%lf\n",*(double *)c_list);
    return sum;
}
int main()
{
    double sum = add(3,1.0,2.0,3.0);
    return 0;
}

结果

1.000000

总结

可变参数列表的参数个数一定不能省略,传参数据类型一定要与va_arg函数中的一致,注意形参中数据类型自动提升。读取形参数据只能顺序读取,不可以跳过某个值。


微信号

相关文章

  • 可变参数列表

    可变参数列表 数组可以向可变参数传值,反之,不行 可变参数列表作为参数时只能放在参数的最后面 可变参数列表的方法是...

  • 三、可变参数列表

    三、可变参数列表

  • Go语言可变参数

    可变参数 参数数量可变的函数称为可变参数函数 在声明可变参数函数时,需要在参数列表的最后一个参数类型之前加上省略符...

  • golang学习笔记之-函数可变参数

    语法 注意点: 如果函数的参数是可变参数,同时还有其他的参数。可变参数要放在形参列表的最后 一个函数的参数列表中,...

  • Lua可变参数

    Lua 函数可以接受可变数目的参数,和 C 语言类似,在函数参数列表中使用...表示函数有可变的参数。 把可变参数...

  • 2020-03-18--方法

    1.重载:方法名相同,参数列表不同,为重载 返回值类型不作为判断标准 2.可变参数列表 有>=1个参数 可变参数放...

  • Kotlin语法杂记

    1.可变参数规则 可变参数不必是函数的参数列表中的最后一个 用vararg paramName: paramTyp...

  • 可变参数列表

    在使用函数函数时,函数得形参并没有确定,这时就需要一个可变形参的出现。可变参数列表完全符合这个要求。其实,在一开始...

  • C++常用的宏

    可变参数列表 VA_ARGS用来表示可变参数列表 宏里面预设的变量 ANSI C标准中有几个重用标准预定义宏:LI...

  • java的可变参数列表

    关于java的可变参数列表 1.简介: java的可变参数列表是的从jdk1.5开始新增的,其英文名词vararg...

网友评论

    本文标题:可变参数列表

    本文链接:https://www.haomeiwen.com/subject/qxvouhtx.html