前言

本文介绍下X宏的使用

首先简单介绍下宏的几种用法

#define    STRCAT(X,Y)    X##Y
#define _STR(X) #@X
#define STR(X) #X
#define Log(...) {printf(__VA_ARGS__);}
/*
* x##y 拼接xy
* #@x 单引号包裹'x'
* #x 字符串化,双引号包裹"x"
* __VA_ARGS__会扩展参数...
*/

ANSI C标准中有几个标准预定义宏(也是常用的):

LINE:在源代码中插入当前源代码行号;

FILE:在源文件中插入当前源文件名;

DATE:在源文件中插入当前的编译日期

TIME:在源文件中插入当前编译时间;

STDC:当要求程序严格遵循ANSI C标准时该标识被赋值为1;

__cplusplus:当编写C++程序时该标识符被定义。常常被用来判断编译器是否符合C++11,C++14等标准,方便运用新特性

编译器在进行源码编译的时候,会自动将这些宏替换为相应内容。

举个例子大家体会下

#define log_err(M,...) fprintf(stderr,"%s %s [ERR]"M"\n",__FILE__,__LINE__,##__VA_ARGS__)

X宏

我们首先看一个日志系统的场景

enum LogLevel {DEBUG,INFO,WARN,ERROR,UNKNOW=-1};

string getLogLevel(LogLevel level) {
switch(level) {
case DEBUG:
return "DEBUG";
break;
case INFO:
return "INFO";
break;
case WARN:
return "WARN";
break;
case ERROR:
return "ERROR";
break;
default:
return "UNKNOW";
}
}

我们定义了一个结构体,表示日志级别。然后定义了一个函数,返回一个字符串,用于获取日志级别对应的名称。

例子比较简单,但我们发现,这个代码有“重复”的部分。case的每个条件的写法是相似。既然格式相同,那么我们就可以简化:

#define X(name) \
case name: \
return #name; \
break; string getLogLevel(LogLevel level) {
switch(level) {
X(DEBUG)
X(INFO)
X(WARN)
X(ERROR) default:
return "UNKNOW";
}
}

前面也说过了,#name是字符串化,name如果是INFO的话,#name就会转化成"INFO"

看上去代码已经清爽多了。为避免其他地方也用到X宏,我们可以在使用完毕立刻undef掉,宏就写在函数内,使代码更加紧凑,改进后如下:

string getLogLevel(LogLevel level) {
switch(level) {
#define X(name) \
case name: \
return #name; \
break; X(DEBUG)
X(INFO)
X(WARN)
X(ERROR)
#undef X default:
return "UNKNOW";
}
}

这样代码就紧凑多了。至于为什么这种宏叫X没考究过,可能是约定俗成的习惯吧。

最后留个思考,getLogLevel能不能也用X宏去实现?

提示:不用函数,直接用一个const char*[]数组去返回日志级别对应的字符串

答案参考如下:

#define mapLogLevel	\
X(DEBUG, "DEBUG") \
X(INFO, "INFO") \
X(WARN, "WARN") \
X(ERROR, "ERROR") \
X(UNKNOW, "UNKNOW") #define X(a, b) a,
enum LogLevel { mapLogLevel };
#undef X #define X(a, b) b,
const char* strLogLevel[] = { mapLogLevel };
#undef X int main(){ LogLevel level=INFO;
cout<<strLogLevel[level]<<endl;
return 0;
}

本文章同时更新微信公众号pusidun,欢迎关注

C语言宏技巧 X宏的更多相关文章

  1. C奇淫技巧,——宏神奇

    一个.宏列表 当这个问题面临: 有一个标志变量.位代表对应的含义. 我们须要提供一组函数来訪问设置这些位.可是对于每一个标记位的操作函数都是相似的.若有32个位,难道要搞32套相似的操作函数么? 你或 ...

  2. Linux C编程学习之C语言简介---预处理、宏、文件包含……

    C的简介 C语言的结构极其紧凑,C语言是一种模块化的编程语言,整个程序可以分割为几个相对独立的功能模块,模块之间的相互调用和数据传递是非常方便的 C语言的表达能力十分强大.C语言兼顾了高级语言和汇编语 ...

  3. C语言 预处理二(宏定义--#define)

    //#define 宏定义(宏定义一般大写) //知识点一-->#define的作用域:从#define开始,从上往下,如果遇到#undef就到#undef处结束,如果没有就是作用于当前整个文件 ...

  4. C语言宏定义和宏定义函数

    要写好C语言,漂亮的宏定义是非常重要的.宏定义可以帮助我们防止出错,提高代码的可移植性和可读性等. 在软件开发过程中,经常有一些常用或者通用的功能或者代码段,这些功能既可以写成函数,也可以封装成为宏定 ...

  5. 一起talk C栗子吧(第一百二十四回:C语言实例--内置宏)

    各位看官们,大家好,上一回中咱们说的是显示变量和函数地址的样例,这一回咱们说的样例是:内置宏.闲话休提,言归正转.让我们一起talk C栗子吧! 看官们,我们在编译程序的时候,假设有语法错误,编译器就 ...

  6. C语言面试题分类->宏定义

    1.写一个“标准”宏,这个宏输入两个参数并返回较小的一个 答:#define MIN(x, y) ((x)<(y)?(x):(y))//注意x,y要加括号,因为x,y如果有复合运算会出现问题. ...

  7. C语言学习笔记 函数式宏

    不学C光搞PHP不知道还有这种东西-函数式宏,宏前面学过了Macro,编译器在对代码进行编译时会对宏表达式进行展开替换,这样宏就起到了全局变量的作用,这里函数式宏也是类似,编译器进行编译时按函数表达是 ...

  8. C 语言常用方法技巧

    C语言常用方法技巧 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !impor ...

  9. Excel VBA(宏):添加宏

    写在前面: .编写宏,打开VBA,双击ThisWorkbook对当前工作薄进行编写宏:双击Sheet1,对整个sheet编写宏: 或者创建模块,在模块里,编写.调试代码. 打开VBA的方法见第一讲,结 ...

随机推荐

  1. PAT 1029 Median (25分) 有序数组合并与防坑指南

    题目 Given an increasing sequence S of N integers, the median is the number at the middle position. Fo ...

  2. vue采用history路由的服务器部署问题

    发现部署问题 在部署的时候发现打开的页面是空白 之前部署原理 之前的页面都是作为静态文件形式打包上传到服务器上 http://www.xiedashuaige.cn/bolg2.0/#/home 就和 ...

  3. 分布式事务解决方案Seata

    Seata全称是Simple Extensible Autonomous Transaction Architecture,是由阿里巴巴开源的具有高性能和易用性的分布式事务解决方案. 微服务中的分布式 ...

  4. PHP AES加解密(兼容php5,php7)

    最近在工作中负责对接API,对方要求对业务数据进行AES 算法(256,ECB,补码方式:PKCS5Padding)加密. 加密算法要求如下: 算法AES/ECB/PKCS5Padding 密钥长度2 ...

  5. jQuery-简单理解

    1.概念 jQuery是js的一个类库,主要封装的是js中DOM操作部分,使用和原生js一样 2.代码展示 HTML部分 封装原理 test测试 JS部分 //声明对象 var bjsxt = {}; ...

  6. Chisel3 - Tutorial - Adder

    https://mp.weixin.qq.com/s/SEcVjGRL1YloGlEPSoHr3A   位数为参数的加法器.通过FullAdder级联实现.   参考链接: https://githu ...

  7. 撸一个简单的vue-router来剖析原理

    理解 随着前端业务的发展, 我们一般在写一个较为大型的vue项目时候,会使用到vue-router,来根据指定的url或者hash来进行内容的分发,可以达到不像服务端发送请求,就完成页面内容的切换,能 ...

  8. 数据库之 MySQL --- 数据处理 之 表的约束与分页(七)

    个人博客网:https://wushaopei.github.io/    (你想要这里多有)     1.约束 :为了保证数据的一致性和完整性,SQL规范以约束的方式对表数据进行额外的条件限制 ​ ...

  9. python3 pymysql查询结果包含字段名

    python2使用MySQLdb模块进行连接mysql数据库进行操作:python3则使用pymysql模块进行连接mysql数据库进行操作:两者在语法上有稍微的差别,其中就包括查询结果包含字段名,具 ...

  10. js数据劫持 Object.defineProperty() 作用

    原生js Object.defineProperty() 作用 假设我们有一个obj对象,我们要给他设置一个name属性会这么做 Object.defineProperty()也可以设置对象属性 这个 ...