函数可变参数列表的套路

本文对比了固定参数与可变参数在C语言中的使用方式,重点介绍了如何使用stdarg.h来处理数量不定的参数列表,并给出了具体的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注:所使用环境为VS2015


在刚开始学习C语言的时候,一般写的代码每个函数的参数肯定都是给定的,给定两个实参,那在函数调用后那就只能使用者两个参数,如果二者的数目不统一那么编译器就会报错。但是如果使用了可变参数那么就不需要担心这些,而且够以一种良好定义的方法访问数量未定的参数列表。接下来就给出多个数求平均值的实现来进行比较。

第一种:给定参数


#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <Windows.h>

int average(int n1, int n2, int n3, int n4)
{
	return (n1 + n2 + n3 + n4) / 4;
}

int main()
{
	int aver = average(2, 3, 4, 3);
	printf("average = %d\n", aver);
	system("pause");
	return 0;
}


这就是我们最初经常写的代码,这样的代码当我们写形参时十分麻烦,如果在实参数目有所改变的时候,对应函数的形参也要更改,有更好的方法干嘛不用呢对吧~接下来就是可变参数的写法


第二种:可变参数(代码后面有详细的解释~)

要想使用可变参数必须调用<stdarg.h>头文件


#define _CRT_SECURE_NO_WARNINGS 1

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

int average(int n, ...)
{
	int sum = 0;
	int avg = 0;
	int i = 0;
	va_list arg;
	va_start(arg, n);
	for (i = 0; i < n; i++)
	{
		sum += va_arg(arg, int);
	}
	avg = sum / n;
	va_end(arg);
	return avg;
}

int main()
{
	int aver = average(4, 2, 3, 4, 3);
	printf("average = %d\n", aver);
	system("pause");
	return 0;
}

先用图简单的解释下函数的可变参数写法:


我们没有见过的就是图中四个框中的内容,下面一个一个解释:

1:va_list arg;

在VS2015中我们可以按F12转到定义;所以我们就看看va_list的定义:


所以其实就是定义了一个char* 指针,至于名字可以自己添加;

2:va_start(arg,n);

第一次转定义后,跳到了下面的定义:


我们选定__crt_va_start再次跳转,


这才是他们的真面目!!!我们发现其实他们都是宏定义,宏定义在使用的过程中,直接将参数全部替换即可,

如果对宏不了解的,也可以看看另一篇博客 http://blog.youkuaiyun.com/sssssuuuuu666/article/details/69943616 ;下来我们就进行替换的操作。


其中的_ADDRESSOF作用就相当于取地址操作&,_INTSIZEOF相当于提升到一个int的大小,如果n是1~4那么结果就是4;5~8就是8;9~12就是12,以此类推;

所以这句话就可以说是将n的地址给char*,然后再加上_INTSIZEOF(n)的大小;这样说又会不明白加上有什么用,函数实参和形参在栈帧中的调用过程也是十分重要的,假设下面是高地址,上面是低地址,形参在栈中是先压最右边的参数,由于栈空间是从高地址向低地址写入,所以最上面的也就是n,我们知道了n的地址通过加地址就知道了在n下面的参数地址。大概用图画一下:


3:va_arg(arg, int):

有了前面的准备,接下来就是取参数的过程了,括号内的arg是自己定义的名字,还是和上面一样我们跳转到最内部的定义然后进行化简:


当我看到最后化简出的结果真的是!

又加4又减4是要干嘛!只能慢慢的再缕了。。

经过第二步我们知道现在char* arg里面已经是未知参数中的第一个参数的地址了,

arg += 4)表示将arg加4也就是指向第二个元素,跳过第一个未知参数,此时(arg+=4)这个表达式的结果是第二个参数的地址,出了括号再减去4又得到了第一个未知参数的地址,此时arg已经指向了下一个参数,但是得到的却是第一个未知参数的地址,从而为下一次获取参数做准备。


4:va_end(arg);


va_end(arg)的作用就是将arg指针置为NULL,到此整个过程就完了~剩下的就都能看得懂了。


如有错误,请联系我谢谢~



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值