文章目录
一、基本使用
首先看下面代码:
#include <stdio.h>
int GetMax(int x, int y)
{
if (x > y)
{
return x;
}
return y;
}
int main()
{
int x = 0;
int y = 0;
scanf("%d %d", &x, &y);
int max = GetMax(x, y);
printf("max = %d\n", max);
return 0;
}
这个很容易理解,求两个数当中的最大值,但是此时我想要求不确定的元素个数的大小,就是说我们不知道元素的个数。那么应该怎么办呢?
可变参数我们C语言中一直在用,比如说:scanf 和 printf
其中…就代表了可变参数。这就很容易理解了,我们在C语言中使用输入输出函数的时候,不知道元素的个数,但是依然还是能够成功运行。
那么我们也可以设计一个函数类似scanf 和 printf一样。
看一下它的使用:
#include <stdio.h>
#include <windows.h>
//可变参数列表至少要有一个参数
int FindMax(int num, ...)//...可变参数列表
{
va_list arg; //定义可以访问可变参数部分的变量,其实是一个char* 类型
va_start(arg, num); //使arg指向可变参数部分
int max = va_arg(arg, int); //根据类型,获取可变参数列表中的第一个数据
for (int i = 0; i < num - 1; i++) //获取并比较其他的
{
int x = va_arg(arg, int);
if (max < x)
{
max = x;
}
}
va_end(arg);//arg使用完毕,收尾工作。本质就是讲arg指向NULL
return max;
}
int main()
{
int max = FindMax(5, 1, 2, 3, 4, 5);//查找5个数当中的最大值
printf("max = %d\n", max);
return 0;
}
注意:可变参数列表至少有一个参数
二、原理
首先还是这串代码,我们一步一步分析
#include <stdio.h>
#include <windows.h>
//可变参数列表至少要有一个参数
int FindMax(int num, ...)//...可变参数列表
{
va_list arg; //定义可以访问可变参数部分的变量,其实是一个char* 类型
va_start(arg, num); //使arg指向可变参数部分
int max = va_arg(arg, int); //根据类型,获取可变参数列表中的第一个数据
for (int i = 0; i < num - 1; i++) //获取并比较其他的
{
int x = va_arg(arg, int);
if (max < x)
{
max = x;
}
}
va_end(arg);//arg使用完毕,收尾工作。本质就是讲arg指向NULL
return max;
}
int main()
{
int max = FindMax(5, 1, 2, 3, 4, 5);//查找5个数当中的最大值
printf("max = %d\n", max);
return 0;
}
第一个就是va_start,是一个char*的指针
然后就是va_end,作用就是将指针置为空
然后通过汇编角度来理解:
此时我们将整形改为字符型:
#include <stdio.h>
#include <windows.h>
//可变参数列表至少要有一个参数
int FindMax(int num, ...)//...可变参数列表
{
va_list arg; //定义可以访问可变参数部分的变量,其实是一个char* 类型
va_start(arg, num); //使arg指向可变参数部分
int max = va_arg(arg, int); //根据类型,获取可变参数列表中的第一个数据
for (int i = 0; i < num - 1; i++) //获取并比较其他的
{
int x = va_arg(arg, int);
if (max < x)
{
max = x;
}
}
va_end(arg);//arg使用完毕,收尾工作。本质就是讲arg指向NULL
return max;
}
int main()
{
//int max = FindMax(5, 1, 2, 3, 4, 5);//查找5个数当中的最大值
int max = FindMax(5, 'a', 'b', 'c', 'd', 'e');//查找5个数当中的最大值
printf("max = %d\n", max);
return 0;
}
为什么可以呢?int max = va_arg(arg, int)难道不应该改为int max = av_arg(arg, char)吗?不是应该按照一个字节读取吗?
我们同样可以转到反汇编:
在往下走一步:
可见在可变参数压栈的时候,如果是短整形,一般都要进行int类型提升。所以编译器会自动进行整形提升,我们如果改为了char就会出错。
注意事项
- 可变参数必须从头到尾逐个访问。如果你在访问了几个可变参数之后想半途终止,这是可以的,但是,如果你想一开始就访问参数列表中间的参数,那是不行的。
- 参数列表中至少有一个命名参数。如果连一个命名参数都没有,就无法使用 va_start 。
- 这些宏是无法直接判断实际存在参数的数量。
- 这些宏无法判断每个参数的是类型。
- 如果在 va_arg 中指定了错误的类型,那么其后果是不可预测的。