手把手教你实现 C 语言的函数多参默认值 「下」

以下内容为本人的学习笔记,如需要转载,请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/ifnDcV7AKrh6eVihVK9l5A

本文上接《手把手教你实现 C 语言的函数多参默认值 上》,下文提及的一些概念来源于上文,为方便阅读理解本文内容,建议先了解一下上文内容。

填充默认值

实际使用变长参数宏 fun2() 时,由于参数具有默认值,部分甚至全部参数可以不输入,输入的参数个数范围介于 0 到目标函数 _fun2() 的完整参数个数之间。已输入的参数和默认值被传入转换函数 _funs(),转换函数 _funs() 再从变长参数列表逐个提取,最终未输入的参数会被默认值替代。

假设函数参数数据类型为 val_type,变长参数函数的参数提取一般形式类似如下:

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():

#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 个参数:

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);

输出结果:

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():

void _fun1(int val)
{
    printf("fun inputs val:%d\n", val);
}

仿照变长参数宏 fun2(),对应定义目标函数 _fun1() 的变长参数宏 fun1():

#define fun1(...)           _funs(1, ARGC(__VA_ARGS__), 5, ##__VA_ARGS__)

设定目标函数 _fun1() 的参数默认值为 5。

然后再稍微完善一下转换函数 _funs() 的调用目标函数代码块:

...
switch (param_num_max)
{
case 1:
    _fun1(args[0]);
    break;
case 2:
    _fun2(args[0], args[1]);
    break;

default:
    break;
}
...

再测试一下上面的成果,调用宏 fun1() 分别输入 0 个、1 个参数:

printf("fun1()\r\n");
fun1();
printf("---\r\n");
printf("fun1(2)\r\n");
fun1(2);

输出结果:

fun1()
real_param_num=0
fun inputs val:5
---
fun1(2)
real_param_num=1
fun inputs val:2

真的实现了“鸡刀宰牛”,有点嗨…


有句话叫“什么对你提出了限制,什么就是你前进的绊脚石”,这样看的话,上面假设的限制条件中限制了参数类型为同一种,是本文未解决的问题,相信聪明的你已经有眉目了。八戒迫切想要和你一起探讨接下来的问题,欢迎联系我!

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值