C 语言 define 变参__VA_ARGS__使用
在C语言的标准库中,printf、scanf、sscanf、sprintf、sscanf这些标准库的输入输出函数,参数都是可变的。在调试程序时,我们可能希望定义一个参数可变的输出函数来记录日志,那么用可变参数的宏是一个不错的选择。
在C99中规定宏也可以像函数一样带可变的参数,如:
#define LOG(format, ...) fprintf(stdout, format, __VA_ARGS__)
其中,...表示可变参数列表,__VA_ARGS__在预处理中,会被实际的参数集(实参列表)所替换。
同时gcc还支持带可以变参数名的方式(注意:VC不支持):
#define LOG(format, args...) fprintf(stdout, format, args)
同样,args在预处理过程中,会被实际的参数集所替换。其用法和上面的方式一样,只是参数的符号有变。
需要注意的是,上述两种方式的可变参数不能省略,尽管可以传一个空参数进去。说到这里,有必要提一下“##”连接符号的用法,“##”的作用是对token进行连接,上例中format,args,__VA_ARGS都可以看作是token,如果token为空,“##”则不进行连接,所以允许省略可变参数。对上述2个示例的改造:
#define LOG(format, ...) fprintf(stdout, format, ##__VA_ARGS__)
#define LOG(format, args...) fprintf(stdout, format, ##args)
即然参数可以省略,那么用宏定义一个开关,实现一个输出日志的函数就简单了:
#ifdef DEBUG
#define LOG(format, ...) fprintf(stdout, ">>>>>" format "<<<<", ##__VA_ARGS__)
#else
#define LOG(format, ...)
#endif
example
#include "stdio.h"
#define LOG_TYPE1(format, ...) do{ 		\
		printf(format, __VA_ARGS__); 	\
										\
} while(0)
#define LOG_TYPE2(format, args...) do{ 	\
		printf(format, args); 			\
										\
} while(0)
#define LOG_TYPE3(format, ...) do{ 		\
		printf(format, ##__VA_ARGS__); 	\
										\
} while(0)
#define LOG_TYPE4(format, args...) do{ 	\
		printf(format, ##args); 		\
										\
} while(0)
#define LOG(x) printf("LOG "#x" %d \n", x);
int value = 10;
int main()
{
	printf("hello world. \n");
	//LOG_TYPE1("hello %d \n");	error
	LOG_TYPE1("hello %d \n", 1);
	//LOG_TYPE2("hello \n"); error
	LOG_TYPE2("hello %d \n", 2);
	LOG_TYPE3("hello 3\n");
	LOG_TYPE3("hello %d\n", 3);
	LOG_TYPE4("hello 4\n");
	LOG_TYPE4("hello %d\n", 4);
	LOG(10);
	LOG(value);
	return 0;
}
C 语言 define 变参__VA_ARGS__使用的更多相关文章
- C语言可变参实现参数累加返回
		C语言可变参的作用真的是非常大,自从发表了可变参如何实现printf,fprintf,sprintf的文章以来,便有不少博友私信问我实现的机制,我也解释了相关的知识点.今天,我们借着这个机会,再来举一 ... 
- c语言可变参
		一.什么是可变参数 在C语言编程中有时会遇到一些参数个数可变的函数,例如printf(),scanf()函数,其函数原型为: int printf(const char* format,…),int ... 
- C语言-define 与do{}while(0)
		问题引出: 我们都知道宏定义#define只是简单替换,所以遇到复杂的带参数宏,必须很小心的为需要的参数加上括号“()”:同样碰到复杂的多条语句替代,虽然加{}可以将其封装成一个整体,但同时又有另一个 ... 
- c语言#define用法
		01 作用域 预编译作用域限本文全局,如: a.c中定义:#define TEST 1 b.c中定义:#define TEST 2 两者编译不交叉,互不影响. 若需#define TEST作用于a.c ... 
- C语言define 可以提高程序运行的速度
		define的优缺点 优点 提高了程序的可读性,同时也方便进行修改: 提高程序的运行效率:使用带参的宏定义既可完成函数调用的功能,又能避免函数的出栈与入栈操作,减少系统开销,提高运行效率: 3.宏是由 ... 
- c语言define和typedef区别和使用
		define完全可以理解替换,typedef代表别名.听着差不多的意思,那2者区别在哪? 先来个简单例子查看基本使用. //define和typedef区别 #define DB double //替 ... 
- C语言 define实现的宏函数汇总
		最大值,最小值 #define MAX( x, y ) ( (x) > (y) ? (x) : (y) )#define MIN( x, y ) ( (x) < (y) ? (x) : ( ... 
- C语言的变参列表  va_list
		1. va_list的基本原理和用法 #include<stdio.h> #include<stdarg.h> void func(int i,char *ch,...){ / ... 
- 【C语言】中的版本规范(C89 C99等)
		C语言中的版本 一.相关基础知识 ISO:国际标准化组织(International Organization for Standardization,ISO)简称ISO,是一个全球性的非政府组织,是 ... 
随机推荐
- java网络编程实现两端聊天
			网络编程的三要素: ip地址:唯一标识网络上的每一台计算机 端口号:计算机中应用的标号(代表一个应用程序),0-1024系统使用或者保留端口,有效端口0-65535(short) 通信协议:通信的规则 ... 
- GitLab Development Kit 环境搭建
			在公司内网服务器上面搭建gdk环境,踩了很多坑,历时四五天(中间涉及申请开通固定外网),整理如下: 总览: 操作系统:redhat 6.3 参考文档:https://gitlab.com/gitlab ... 
- 【Centos7】Delete virtual bridge
			Previously,Stop service which controls virtual bridges. sudo systemctl stop libvirtd.service #System ... 
- dotnet使用Selenium执行自动化任务
			如果要做百度文库,百度贴吧,百度知道签到,你,会怎么做?前不久我还会觉得这好像太麻烦了,now,soeasy. 自动化测试工具:Selenium Selenium是一个用于Web应用程序测试的工具.S ... 
- Integer陷阱(0~127和其他    数值相等对象比较)
			Integer 类在对象中包装了一个基本类型 int 的值. 有一个陷阱存在,经常出现在面试题中,情况如下面代码 public class IntegerDemo { public static vo ... 
- Oracle 11g的安装
			选择对应版本的Oracle 11g: 解压之后,点击"setup.exe"进行安装. 如果希望接受Oracle的广告邮件可以留下自己的电子邮件地址, 如果希望通过My Oracle ... 
- 关于 DropDownList 循环绑定中遇到的问题
			1绑定DataTable 简单直接下部分就ok了 this.DropDownList1.DataSource = DataTable; this.DropDownList1.DataTextField ... 
- Form表单提交,Ajax请求,$http请求的区别
			做过前端同学想必都避免不了要和后台server打交道.而以下这三种与后台交互的方式想必大家都不陌生. Form表单提交,Ajax请求,Angular的$http请求 以前一直搞不清楚什么时候应该用哪种 ... 
- python中列表 元组 字典 集合的区别
			列表 元组 字典 集合的区别是python面试中最常见的一个问题.这个问题虽然很基础,但确实能反映出面试者的基础水平. (1)列表 什么是列表呢?我觉得列表就是我们日常生活中经常见到的清单.比如,统计 ... 
- Android 7.0  Power 按键处理流程
			Android 7.0 Power 按键处理流程 Power按键的处理逻辑由PhoneWindowManager来完成,本文只关注PhoneWindowManager中与Power键相关的内容,其他 ... 
