【预处理】——获取可变参数宏的参数数量

文章目录

    • 功能说明
    • 实现
      • `...` 的作用
      • `__VA_ARGS__` 的作用
      • `##__VA_ARGS__` 的作用
    • 解析
      • COUNT_ARGS(2, 4, 5)
      • 没有参数 COUNT_ARGS()
      • 参数大于 22 个
    • 示例

功能说明

用于获取可变参数宏实际传递了多少个参数。

  • COUNT_ARGS(1, 2, 3),填入了 3 个参数,返回值就是 3
  • COUNT_ARGS("Hello", 'a', 3.14, 4),填入了 4 个 参数,返回值就是 4
  • COUNT_ARGS(1),我们填入了 1 个参数,返回值就是 1

实现

#define __COUNT_ARGS(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, N, ...) N
#define COUNT_ARGS(...) __COUNT_ARGS(, ##__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
  1. __COUNT_ARGS 宏定义了一个带有多个参数的宏,其中 _0 到 _20 是占位符参数, N 表示参数个数。
  2. __COUNT_ARGS 宏使用了变参宏的特性,通过 ## 将参数序列转换为逗号分隔的参数列表。
  3. COUNT_ARGS 宏定义了一个变参宏,它使用 __COUNT_ARGS 宏来计算参数个数。
  4. COUNT_ARGS 宏通过添加一个空参数 (,) 来处理没有参数的情况,避免编译错误。
  5. COUNT_ARGS 宏最多可以接受 20 个参数,如果超过 20 个参数,则只会计算前 20 个参数的个数。

... 的作用

... 是一个可变参数宏的语法,它表示可以接受任意数量的参数。在宏定义中,... 表示参数列表的结束,可以在宏的定义中使用这些参数。

__VA_ARGS__ 的作用

  • __VA_ARGS__ 是一个特殊的宏,它表示可变参数的占位符。在宏定义中, __VA_ARGS__ 会被替换为传递给宏的实际参数。它可以用于将可变参数传递给其他宏或函数。

##__VA_ARGS__ 的作用

##__VA_ARGS__ 是在可变参数宏中使用的一个特殊语法。它的作用是在宏展开时,将可变参数的逗号连接符去除,以避免出现多余的逗号。这在某些情况下非常有用,特别是当宏的参数列表为空时。

当我们没有参数向 COUNT_ARGS 传递时,__VA_ARGS__ 的值为空,, ##__VA_ARGS__ 这部分会被删除。这里是使用了 ## 的语法,当 __VA_ARGS__ 为空字符串时,会连同最开始的 , 被一并删除了。

解析

#define __COUNT_ARGS(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, N, ...) N

这个宏要求至少传递 22 个参数,返回值就是这第 22 个参数。

如果超过第 22 个的参数,会被 … 给消化,并且返回第 22 个参数。

其实该宏的本质是先用 20, 19, ... 1, 0 占位,每多加一个参数,最右边的数就会被挤掉。

增加一个参数,0 就被挤掉,返回第 22 个参数就是 1
增加两个参数,0 和 1就被挤掉,返回第 22 个参数就是 2
以此类推

COUNT_ARGS(2, 4, 5)

COUNT_ARGS(2, 4, 5) 展开后就变为

__COUNT_ARGS(, 2, 4, 5, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

传递了 25 个参数给 __COUNT_ARGS ,接下来我们再与 __COUNT_ARGS 里的 形参 进行对应:

__COUNT_ARGS 宏的作用是返回实参的第 22 个参数,实际 __COUNT_ARGS 传递了 25 个参数 ,所以该宏只返回第 22 个参数即 3

没有参数 COUNT_ARGS()

COUNT_ARGS() 展开后就变为

__COUNT_ARGS(, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

有 22 个参数传递给 __COUNT_ARGS ,然后传递的参数与 __COUNT_ARGS 里的形参进行一一对应:
__COUNT_ARGS 宏的作用是返回实参的第 22 个参数,实际 __COUNT_ARGS 传递了 22 个参数 ,所以该宏只返回第 22 个参数即 0

参数大于 22 个

另外要注意传入的参数不能超过 22 个。如果传入的参数大于 22 个,那么这个宏将返回第 22 个参数的参数值。

例如

COUNT_ARGS(c)

宏展开后就是

(, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 46, 76, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

传递了 44 个参数给 __COUNT_ARGS ,接下来我们再与 __COUNT_ARGS 里的 形参 进行对应:

__COUNT_ARGS 宏的作用是返回实参的第 22 个参数,实际 __COUNT_ARGS 传递了 44 个参数 ,所以该宏只返回第 22 个参数即 46

所以当参数超过 22 个之后,不管参数的数量是多少,N 对应的永远是第 22 个参数的值。

示例

#include <stdio.h>#define __COUNT_ARGS(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, N, ...) N
#define COUNT_ARGS(...) __COUNT_ARGS(, ##__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)int main(int argc, char *argv[])
{int count1 = COUNT_ARGS();                                                                              // count1 = 0int count2 = COUNT_ARGS(1, 2, 3);                                                                       // count2 = 3int count3 = COUNT_ARGS(1, 2, 3, 4, 5, 6, 7);                                                           // count3 = 7int count4 = COUNT_ARGS("Hello", 'a', 3.14);                                                            // count4 = 3int count5 = COUNT_ARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);         // count5 = 20int count6 = COUNT_ARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 46, 76); // count6 = 20printf("count1 = %d\n", count1);printf("count2 = %d\n", count2);printf("count3 = %d\n", count3);printf("count4 = %d\n", count4);printf("count5 = %d\n", count5);printf("count6 = %d\n", count6);return 0;
}

结果打印

count1 = 0
count2 = 3
count3 = 7
count4 = 3
count5 = 20
count6 = 46

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#中未能找到为main方法指定的XXX.Program怎么解决

有时在修改项目名称后&#xff0c;报错未能找到为main方法指定的XXX.Program 解决办法&#xff1a; 点击进入项目属性&#xff0c;将启动对象设置为空或者你要指定的XXX.Program&#xff08;改名后的&#xff09;

akka 简单使用

由于AKka的核心是Actor&#xff0c;而Actor是按照Actor模型进行实现的&#xff0c;所以在使用Akka之前&#xff0c;有必要弄清楚什么是Actor模型。 Actor模型最早是1973年Carl Hewitt、Peter Bishop和Richard Seiger的论文中出现的&#xff0c;受物理学中的广义相对论(general…

服务器数据恢复-误操作导致存储VDisk丢失的数据恢复案例

服务器数据恢复环境&#xff1a; IBM某型号存储&#xff1b; Solaris操作系统&#xff0c;部署Oracle数据库。 服务器故障&#xff1a; 重建MDisk导致对应的存储池中的VDisk丢失&#xff0c;导致Solaris操作系统中的Oracle数据库无法使用。 服务器数据恢复过程&#xff1a; 1、…

理解Android中不同的Context

作者&#xff1a;两日的blog Context是什么&#xff0c;有什么用 在Android开发中&#xff0c;Context是一个抽象类&#xff0c;它是Android应用程序环境的一部分。它提供了访问应用程序资源和执行各种操作的接口。可以说&#xff0c;Context是Android应用程序与系统环境进行交…

面向对象——步入JavaScript高级阶段的敲门砖

目录 前言一、认识对象1.什么是对象2.对象的方法3.对象的遍历4.对象的深浅克隆 二、认识函数上下文1.函数的上下文规则 ★2.call和apply ★ 三、构造函数1.用new操作符调用函数2.类与实例3.构造函数和类" 四、原型和原型链1.prototype和原型链查找 ★2.在prototype上添加方…

Windows下安装Hadoop(手把手包成功安装)

Windows下安装Hadoop&#xff08;手把手包成功安装&#xff09; Windows下安装Hadoop&#xff08;手把手包成功安装&#xff09;一、环境准备1.1、查看是否安装了java环境 二、下载Hadoop的相关文件三、解压Hadoop安装包四、替换bin文件夹五、配置Hadoop环境变量六、检查环境变…

【数学建模】时间序列分析

文章目录 1. 条件2. 模型分类3. SPSS处理时间序列 1. 条件 1.使用于具有时间、数值两种要素 2.数据具有周期性可以使用时间序列分解 2. 模型分类 叠加模型【YTSCI】 序列的季节波动变化越来越大&#xff0c;反映变动之间的关系发生变化乘积序列【YTSC*I】 时间序列波动保持恒…

【多模态】16、DetCLIP | 构建超大词汇字典来进行开放世界目标检测

论文&#xff1a;DetCLIP: Dictionary-Enriched Visual-Concept Paralleled Pre-training for Open-world Detection 代码&#xff1a;无。。。 出处&#xff1a;NIPS2022 | 华为诺亚方舟 | 中山大学 | 香港科技大学 效果&#xff1a; 在 LVIS 的 1203 个类别上超越了 GLIP…

【树上乘法原理】ICPC Shanghai 2021 G, Edge Groups

http://oj.daimayuan.top/course/8/problem/400 题意&#xff1a; 思路&#xff1a; 求方案数&#xff0c;考虑组合数学 手摸一下样例发现&#xff0c;对于一棵子树&#xff0c;若边数为偶数&#xff0c;那么可以内部匹配&#xff0c;但是如果边数为奇数&#xff0c;那么就一…

嵌入式:QT Day2

一、继续完善登录框&#xff0c;当登陆成功时&#xff0c;关闭登陆页面&#xff0c;跳转到新的界面中 源码&#xff1a; widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QDebug> //用于打印输出 #include <QIcon> …

无涯教程-jQuery - jQuery.getScript( url, callback )方法函数

jQuery.getScript(url&#xff0c;[callback])方法使用HTTP GET请求加载并执行JavaScript文件。 该方法返回XMLHttpRequest对象。 jQuery.getScript( url, [callback] ) - 语法 $.getScript( url, [callback] ) 这是此方法使用的所有参数的描述- url - 包含请求…

mac 移动硬盘未正常退出,再次链接无法读取(显示)

&#xff08;1&#xff09;首先插入自己的硬盘&#xff0c;然后找到mac的磁盘工具 &#xff08;2&#xff09;打开磁盘工具&#xff0c;发现自己的磁盘分区在卸载状态&#xff1b;点击无法成功装载。 &#xff08;3&#xff09;打开终端&#xff0c;输入 diskutil list查看自…

VITE + VUE 全局环境变量使用相关知识

一. 全局环境变量配置及使用 配置&#xff1a; 创建.env和.env.development&#xff0c;.env.production等配置文件&#xff0c;.env.uat, .env.test及其它均可。变量要以大写的VITE_开头&#xff0c; 如VITE_APP_NAME 测试网站 使用&#xff1a; 在代码中 import.meta.env…

Redis应用(1)——生成全局唯一标识ID

1 概述 在实际项目中&#xff0c;根据不同的业务逻辑需要生成唯一的标识id &#xff0c;如购买商品生成的订单号。尽管这个标识id功能非常的简单&#xff0c;但是如果不能成功的生成唯一标识id&#xff0c;那将会影响后续的业务逻辑 。我们可以使用数据库去生成唯一标识id&…

ajax相关

1、HTTP 协议1.1、作用 规范了数据是如何打包的 以及 数据时如何传递的1.2、Message 消息 / 报文 Message 指的是在HTTP客户端与服务器间传递的数据块 分类&#xff1a; 1、Request Message : 客户端向服务器发送的请求消息 2…

经营在线业务的首选客服工具--SS客服

随着网购正在快速取代传统零售业&#xff0c;各行各业的企业都在大力发展电子商务以取悦客户。但是&#xff0c;有这么多可用的电子商务平台&#xff0c;选择一款符合自己发展的平台确实不容易。电子商务平台不仅是企业在线销售产品和服务的地方&#xff0c;也是他们管理日常运…

Qt Core学习日记——第七天QMetaObject(上)

每一个声明Q_OBJECT的类都具有QMetaObject对象 Q_OBJECT宏源代码&#xff1a; #define Q_OBJECT \ public: \ QT_WARNING_PUSH \ Q_OBJECT_NO_OVERRIDE_WARNING \ static const QMetaObject staticMetaObject; \ virtual const QMetaObject *metaObject() const; \ vir…

深度学习入门教程(2):使用预训练模型来文字生成图片TextToImageGenerationWithNetwork

本深度学习入门教程是在polyu HPCStudio 启发以及资源支持下进行的&#xff0c;在此也感谢polyu以及提供支持的老师。 本文内容&#xff1a;在GoogleColab平台上使用预训练模型来文字生成图片Text To Image Generation With Network &#xff08;1&#xff09;你会学到什么&a…

【项目实战】日志系统

文章目录 日志系统1、项目介绍2、开发环境3、核心技术4、日志系统介绍4.1 日志系统的价值4.2 日志系统技术实现 5、相关技术知识5.1 不定参函数5.1.1 不定参宏函数5.1.2 C 风格不定参数使用5.1.3 C风格不定参数使用 5.2 设计模式5.2.1六大原则5.2.2 单例模式5.2.3 工厂模式5.2.…

Fiddler使用教程|渗透测试工具使用方法Fiddler

提示&#xff1a;如有问题可联系我&#xff0c;24小时在线 文章目录 前言一、Fiddler界面介绍二、菜单栏1.菜单Fiddler工具栏介绍Fiddler命令行工具详解 前言 网络渗透测试工具&#xff1a; Fiddler是目前最常用的http抓包工具之一。 Fiddler是功能非常强大&#xff0c;是web…