手把手教你实现 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

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


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/559587.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用LLM-API开发应用-DataWhale笔记

调用API 先使用一个例子 from openai import OpenAI ​ client OpenAI(# This is the default and can be omittedapi_keyos.environ.get("OPENAI_API_KEY"), //这个在环境env中 ) ​ completion client.chat.completions.create(# 调用模型&#xff1a;ChatGPT-…

【目标检测】YOLO系列-YOLOv1 理论基础 通俗易懂

为方便大家理解YOLO的原理&#xff0c;这里将YOLOv1的部分内容基础内容进行用比较直白的话和例子进行阐述&#xff0c;为后续大家学习YOLO作为铺垫。 1、模型所干的活 工作中&#xff0c;大家经常将 Word 文档 上传到某转换器&#xff0c;然后转换输出为PDF文档。目标检测中我…

单点登录实现:一次登录,到处运行

单点登录&#xff1a;一次登录&#xff0c;到处运行 举个场景&#xff0c;假设我们的系统被切割为N个部分&#xff1a;商城、论坛、直播、社交…… 如果用户每访问一个模块都要登录一次&#xff0c;那么用户将会疯掉&#xff0c; 为了优化用户体验&#xff0c;我们急需一套机制…

组件安全(Solr、Shiro、Log4j、Jackson、FastJson、XStream)

Solr 主要基于HTTP和 Apache Lucene 实现的全文搜索服务器。 特征&#xff1a;图标识别 端口&#xff1a;8393 CVE-2019-0193&#xff08;远程命令执行漏洞&#xff09; 漏洞版本&#xff1a;Apache Solr < 8.2.0 利用条件&#xff1a; Apache Solr 的 DataImportHandler 启…

23-代码随想录第202题快乐数

202. 快乐数 简单 相关标签 相关企业 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。 然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变不到…

【C语言】指针详解(五)

目录 1.字符指针 1.1常量字符串 2.指针数组 3.数组指针 1.字符指针 字符指针就是指向字符的指针&#xff0c;字符指针可以存储字符变量的地址。 举例如下&#xff1a; 定义指针变量pa存a的地址&#xff0c;改变*pa的值&#xff0c;a也会随之改变 。 1.1常量字符串 &#x1f…

电机控制专题(一)——最大转矩电流比MTPA控制

文章目录 电机控制专题(一)——最大转矩电流比MTPA控制前言理论推导仿真验证轻载1Nm重载30Nm 总结 电机控制专题(一)——最大转矩电流比MTPA控制 前言 MTPA全称为Max Torque Per Ampere&#xff0c;从字面意思就可以知道MTPA算法的目的是一个寻优最值问题&#xff0c;可以从以…

PTA L2-047 锦标赛

题目 解析 把每一场比赛看作满二叉树的一个节点&#xff0c;父节点递归遍历子节点的结果&#xff0c;进行试填。 代码 #include <bits/stdc.h>using i64 long long;struct Node {int win, lose; };void solve() {int k;std::cin >> k;int siz (1 << k);…

Llama 3王者归来,可与GPT-4分庭抗礼,开源模型即将追上闭源模型了?

“有史以来最强大的开源大模型”Llama 3引爆AI圈&#xff0c;马斯克点赞&#xff0c;英伟达高级科学家Jim Fan直言&#xff0c;Llama 3将成为AI大模型发展历程的“分水岭”&#xff0c;AI顶尖专家吴恩达称Llama3是他收到的最好的礼物。 4月18日&#xff0c;AI圈再迎重磅消息&a…

一例Mozi僵尸网络的挖矿蠕虫分析(workminer)

概述 这是一个Linux平台的挖矿蠕虫&#xff0c;使用了go和C混合编译而成&#xff0c;主要通过爆破SSH口令进行传播&#xff0c;属于Mozi僵尸网络。其中GO代码负责SSH相关的爆破传播&#xff0c;以及对Config的处理&#xff0c;C代码则负责处理加入Mozi P2P网络&#xff0c;拉取…

【游戏专区】贪吃蛇

1&#xff0c;游戏背景 贪吃蛇&#xff08;Snake&#xff09;是一款经典的电子游戏&#xff0c;最初在1976年由 Gremlin 公司开发。它的游戏背景相对简单&#xff0c;但具有高度的成瘾性。 1. **游戏场景**&#xff1a;通常在一个有界的矩形区域内进行&#xff0c;可以是一个…

设计模式—门面模式

定义: 门面模式,也称为外观模式&#xff0c;是一种结构型设计模式。它的主要目的是提供统一的接口来访问子系统中的多个接口&#xff0c;从而简化客户端与复杂子系统之间的交互。 在门面模式中&#xff0c;一个门面类充当中介&#xff0c;为客户端提供一个简化了的访问方式&…

【Gradio】Could not create share link

【Gradio】Could not create share link 写在最前面在服务器端一直运行一个Python脚本解决&#xff1a;下载frpc_linux_amd64文件&#xff0c;并添加权限原理 完整过程&#xff1a;先找gradio库位置&#xff0c;然后发现缺失文件1. 打开终端2. 使用 find 命令查找 gradio 目录3…

Codigger GT模块:GUI融合Terminal,重塑开发体验

在信息技术日新月异的今天&#xff0c;开发者与计算机系统进行交互的界面&#xff0c;其体验的优化与升级显得尤为关键。Codigger G&T正是应这一需求而生&#xff0c;它巧妙地将现代图形用户界面&#xff08;GUI&#xff09;的优势融入传统的Terminal中&#xff0c;为开发者…

基于Java SpringBoot+Vue的体育用品库存管理系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

链路加密技术:保护数据传输的盾牌

在数字时代&#xff0c;数据安全已经成为我们日常生活和工作中的重要议题。随着网络技术的飞速发展和信息量的爆炸式增长&#xff0c;数据的安全传输变得尤为重要。链路加密技术作为一种重要的网络安全措施&#xff0c;为数据的传输提供了强有力的保障。本文将深入探讨链路加密…

2024第十五届蓝桥杯 Java B组 填空题

声明&#xff1a;博主比较菜&#xff0c;以下均为个人想法。解决方法仅供参考。欢迎大家一起讨论交流&#xff01; 编程题在文末链接 第一题&#xff1a; 题目&#xff1a; &#xff08;简洁版&#xff09;从小到大排列是20或24倍数的正整数&#xff0c;前10个数依次是&…

C语言结课实战项目_贪吃蛇小游戏

目录 最终实现效果&#xff1a; 实现基本的功能&#xff1a; 根据游戏进程解释代码&#xff1a; 游戏初始化&#xff1a; 首先进入游戏&#xff0c;我们应该将窗口名称改为 “贪吃蛇” 并将光标隐藏掉。再在中间打印游戏信息。 之后我们要把地图打印出来&#xff1a; 然后…

【动态规划】C++简单多状态dp问题(打家劫舍、粉刷房子、买卖股票的最佳时机...)

文章目录 前言1. 前言 - 理解动态规划算法2. 关于 简单多状态的dp问题2.5 例题按摩师/打家劫舍 3. 算法题3.1_打家劫舍II3.2_删除并获得点数3.3_粉刷房子3.4_买卖股票的最佳时机含冷冻期3.5_买卖股票的最佳时机含手续费3.6_买卖股票的最佳时机III3.7_买卖股票的最佳时机IV 前言…

开源模型应用落地-chatglm3-6b-gradio-入门篇(七)

一、前言 早前的文章&#xff0c;我们都是通过输入命令的方式来使用Chatglm3-6b模型。现在&#xff0c;我们可以通过使用gradio&#xff0c;通过一个界面与模型进行交互。这样做可以减少重复加载模型和修改代码的麻烦&#xff0c; 让我们更方便地体验模型的效果。 二、术语 2.…
最新文章