【c语法】##__VA_ARGS__与__VA_ARGS__

2024-08-27 00:12
文章标签 语法 args va ##__

本文主要是介绍【c语法】##__VA_ARGS__与__VA_ARGS__,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

欢迎来到 破晓的历程的 博客

⛺️不负时光,不负己✈️

文章目录

    • 引言
    • __VA_ARGS__

引言

在调试过程中,我们经常会自定义打印,比如日志信息的输出,这时就会用VA_ARGS,接下来详细讲解!

VA_ARGS

__VA_ARGS__是C语言设定的一个预定义宏,用于处理可变参数的参数列表。通常用在宏定义中,以便宏可以接收不定数量的参数,并将他们作为整体处理。
通常的函数参数列表都是固定的,但也存在着不定参数数量的函数,如:printf
,为了定义可以处理不同参数个数的宏,C99标准引入了 VA_ARGS,下列的代码均在C99及C99以上标准的环境下运行。

__VA_ARGS__表示所有在宏调用中传递的额外参数。它可以放在宏定义的参数列表的末尾,用于接收任意数量的额外参数,比如:

#define LOG_DEBUG(format,...) printf(format,__VA_ARGS__)

解析:

  • DEBUG_LOG 是一个宏,接受至少一个参数 fmt(格式化字符串),后面可以是任意数量的参数。
  • VA_ARGS 表示可变参数列表,在宏展开时会被替换为传递给 DEBUG_LOG 宏的所有实际参数。
    示例:
#define LOG_(format,...) printf("[%s:%d]"format"\n",__FILE__,__LINE__,__VA_ARGS__);

如上的代码就是对日志定义的一个宏。


##VA_ARGS

大家在了解__VA_ARGS__时,一定会看到有些地方在该宏定义前使用 ## 运算符来处理可变参数,如:

#define DEBUG_LOG(fmt, ...) \printf("[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)

##__VA_ARGS__前面加上##的作用是:当可变参数的个数为0时,这里的##可以把把前面多余的","去掉,否则会编译出错。
当想要在自定义的调试信息加上时间、行数等信息时,应该怎么做呢?先把正确的用法写在前面:

#include <stdio.h>#define LOG3(fmt, ...)          printf("<%s:%s>:"fmt"\r\n", __FILE__, __FUNCTION__, ##__VA_ARGS__)
int main(int argc, char** argv)
{char *str = "test __VA_ARGS__";int num = 10086;LOG3("this is test __VA_ARGS__");LOG3("this is test __VA_ARGS__:%s, %d", str, num);LOG3();return 0;
}

结果为:

<main.c:main>:this is test __VA_ARGS__
<main.c:main>:this is test __VA_ARGS__:test __VA_ARGS__, 10086
<main.c:main>:

但是如果不加##:可变参数为空时,报错。

#include <stdio.h>#define LOG2(fmt, ...)          printf("<%s:%s>:"fmt"\r\n", __FILE__, __FUNCTION__, __VA_ARGS__)
int main(int argc, char** argv)
{LOG2();//不传参数,编译报错LOG2("this is test __VA_ARGS__");//编译报错LOG2("this is test __VA_ARGS__:%s, %d", str, num);//正常编译return 0;
}

分析:

  • 第6行没有传参数,宏定义LOG2(fmt,…)展开后,__VA_ARGS__是空的,这时printf后面剩余一个,,必然编译失败;
  • 第7行虽然传入了字符串,但是该字符串赋值给了LOG2(fmt,…)的第一个参数fmt,宏展开后,printf后面也剩余一个,,所以编译报错。

这篇关于【c语法】##__VA_ARGS__与__VA_ARGS__的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1110080

相关文章

Python正则表达式语法及re模块中的常用函数详解

《Python正则表达式语法及re模块中的常用函数详解》这篇文章主要给大家介绍了关于Python正则表达式语法及re模块中常用函数的相关资料,正则表达式是一种强大的字符串处理工具,可以用于匹配、切分、... 目录概念、作用和步骤语法re模块中的常用函数总结 概念、作用和步骤概念: 本身也是一个字符串,其中

Mysql用户授权(GRANT)语法及示例解读

《Mysql用户授权(GRANT)语法及示例解读》:本文主要介绍Mysql用户授权(GRANT)语法及示例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录mysql用户授权(GRANT)语法授予用户权限语法GRANT语句中的<权限类型>的使用WITH GRANT

HTML5表格语法格式详解

《HTML5表格语法格式详解》在HTML语法中,表格主要通过table、tr和td3个标签构成,本文通过实例代码讲解HTML5表格语法格式,感兴趣的朋友一起看看吧... 目录一、表格1.表格语法格式2.表格属性 3.例子二、不规则表格1.跨行2.跨列3.例子一、表格在html语法中,表格主要通过< tab

Java使用ANTLR4对Lua脚本语法校验详解

《Java使用ANTLR4对Lua脚本语法校验详解》ANTLR是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件,下面就跟随小编一起看看Java如何使用ANTLR4对Lua脚本... 目录什么是ANTLR?第一个例子ANTLR4 的工作流程Lua脚本语法校验准备一个Lua Gramm

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

Python基础语法中defaultdict的使用小结

《Python基础语法中defaultdict的使用小结》Python的defaultdict是collections模块中提供的一种特殊的字典类型,它与普通的字典(dict)有着相似的功能,本文主要... 目录示例1示例2python的defaultdict是collections模块中提供的一种特殊的字

C++ 中的 if-constexpr语法和作用

《C++中的if-constexpr语法和作用》if-constexpr语法是C++17引入的新语法特性,也被称为常量if表达式或静态if(staticif),:本文主要介绍C++中的if-c... 目录1 if-constexpr 语法1.1 基本语法1.2 扩展说明1.2.1 条件表达式1.2.2 fa

C++语法知识点合集:11.模板

文章目录 一、非类型模板参数1.非类型模板参数的基本形式2.指针作为非类型模板参数3.引用作为非类型模板参数4.非类型模板参数的限制和陷阱:5.几个问题 二、模板的特化1.概念2.函数模板特化3.类模板特化(1)全特化(2)偏特化(3)类模板特化应用示例 三、模板分离编译1.概念2.模板的分离编译 模版总结 一、非类型模板参数 模板参数分类类型形参与非类型形参 非类型模板

Java基础回顾系列-第一天-基本语法

基本语法 Java基础回顾系列-第一天-基本语法基础常识人机交互方式常用的DOS命令什么是计算机语言(编程语言) Java语言简介Java程序运行机制Java虚拟机(Java Virtual Machine)垃圾收集机制(Garbage Collection) Java语言的特点面向对象健壮性跨平台性 编写第一个Java程序什么是JDK, JRE下载及安装 JDK配置环境变量 pathHe

Hibernate框架中,使用JDBC语法

/*** 调用存储过程* * @param PRONAME* @return*/public CallableStatement citePro(final String PRONAME){Session session = getCurrentSession();CallableStatement pro = session.doReturningWork(new ReturningWork<C