以下内容为本人的学习笔记,如需要转载,请声明原文链接 微信公众号「ENG八戒」mp.weixin.qq.com/s/ifnDcV7AK...
本文上接《手把手教你实现 C 语言的函数多参默认值 上》,下文提及的一些概念来源于上文,为方便阅读理解本文内容,建议先了解一下上文内容。
填充默认值
实际使用变长参数宏 fun2() 时,由于参数具有默认值,部分甚至全部参数可以不输入,输入的参数个数范围介于 0 到目标函数 _fun2() 的完整参数个数之间。已输入的参数和默认值被传入转换函数 _funs(),转换函数 _funs() 再从变长参数列表逐个提取,最终未输入的参数会被默认值替代。
假设函数参数数据类型为 val_type,变长参数函数的参数提取一般形式类似如下:
ini
va_list valist;
va_start(valist, last_solid_arg);
val_type arg = va_arg(valist, val_type);
va_end(valist);
last_solid_arg 应该是上面代码块所属的函数的最后一个固定参数,否则编译时可能会报警:
warning: second parameter of 'va_start' not last named argument
其实编译器能够自动识别哪个是函数的最后一个固定参数,之所以还要报警,是为了兼容标准,了解这点就可以了。
如果多个参数之间的类型不同,提取参数的步骤会更为复杂。为了简化参数提取,这里统一类型,目标函数 _fun2() 输入参数的类型统一为一种,暂定为 int 类型并且用宏定义表示。
我们看看基于上面的思路实现的转换函数 _funs():
scss
#define FUNN_PARAM_TYPE int
void _funs(int param_num_max, int real_param_num, ...)
{
va_list valist;
// 目标函数的参数列表
FUNN_PARAM_TYPE *args = (FUNN_PARAM_TYPE *)malloc(param_num_max * sizeof(FUNN_PARAM_TYPE));
if (NULL == args) {
printf("malloc for args failure\n");
return;
}
// 默认值列表
FUNN_PARAM_TYPE *defaults = (FUNN_PARAM_TYPE *)malloc(param_num_max * sizeof(FUNN_PARAM_TYPE));
if (NULL == defaults) {
printf("malloc for defaults failure\n");
return;
}
printf("real_param_num=%d\n", real_param_num);
va_start(valist, real_param_num); // 第二个参数应该是所在函数的最后一个固定参数
for (int i = 0; i < 2 * param_num_max; ++ i) {
if (i < param_num_max) {
// 提取默认值
defaults[i] = va_arg(valist, FUNN_PARAM_TYPE);
} else {
if ((real_param_num + param_num_max) > i) {
// 用实际输入参数填充目标函数的参数
args[i - param_num_max] = va_arg(valist, FUNN_PARAM_TYPE);
} else {
// 无输入的参数用默认值填充
args[i - param_num_max] = defaults[i - param_num_max];
}
}
}
va_end(valist);
// 调用目标函数
switch (param_num_max)
{
case 2:
_fun2(args[0], args[1]);
break;
default:
break;
}
free(args);
free(defaults);
}
来测试一下上面的成果,调用宏 fun2() 分别输入 0 个、1 个、2 个参数:
scss
printf("fun2()\r\n");
fun2();
printf("---\r\n");
printf("fun2(7)\r\n");
fun2(7);
printf("---\r\n");
printf("fun2(4, 6)\r\n");
fun2(4, 6);
输出结果:
yaml
fun2()
real_param_num=0
fun inputs val1:123, val2:456
---
fun2(7)
real_param_num=1
fun inputs val1:7, val2:456
---
fun2(4, 6)
real_param_num=2
fun inputs val1:4, val2:6
好了,多参数默认值功能已经实现了,延申一下:上面的代码能不能兼容单参数函数的默认值呢?
我觉得 OK,同样地,先定义一个单参数的目标函数 _fun1():
arduino
void _fun1(int val)
{
printf("fun inputs val:%d\n", val);
}
仿照变长参数宏 fun2(),对应定义目标函数 _fun1() 的变长参数宏 fun1():
scss
#define fun1(...) _funs(1, ARGC(__VA_ARGS__), 5, ##__VA_ARGS__)
设定目标函数 _fun1() 的参数默认值为 5。
然后再稍微完善一下转换函数 _funs() 的调用目标函数代码块:
ini
...
switch (param_num_max)
{
case 1:
_fun1(args[0]);
break;
case 2:
_fun2(args[0], args[1]);
break;
default:
break;
}
...
再测试一下上面的成果,调用宏 fun1() 分别输入 0 个、1 个参数:
scss
printf("fun1()\r\n");
fun1();
printf("---\r\n");
printf("fun1(2)\r\n");
fun1(2);
输出结果:
kotlin
fun1()
real_param_num=0
fun inputs val:5
---
fun1(2)
real_param_num=1
fun inputs val:2
真的实现了"鸡刀宰牛",有点嗨...
有句话叫"什么对你提出了限制,什么就是你前进的绊脚石",这样看的话,上面假设的限制条件中限制了参数类型为同一种,是本文未解决的问题,相信聪明的你已经有眉目了。八戒迫切想要和你一起探讨接下来的问题,欢迎联系我!