前言

本文介绍下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. net core获取appsetting.json的另外一种思路(全局,实时变化无需重启项目)

    最近在写net core的项目,在非controller和service里面需要用到appsetting.json文件里面的一些配置,查资料大概有几种思路: 注入,然后config.GetSectio ...

  2. pycharm激活码,亲测有效

    PyCharm是由著名的JetBrains公司所打造的一款功能强大的Python IDE,它具有一般IDE都具备的功能, 并且使用起来非常方便好用.最近需求PyCharm激活码的网友非常多,就在这里给 ...

  3. linux高级管理第十二章--rsync

    实验部分 1.安装rsync 2.配置文件 3.配置密码 4.后续 5.为了测试,创建几个文件 配置实时同步 1.调整inotify内核参数 安装inotify-tools 测试同步 编写脚本 验证 ...

  4. 2.Linux系统之硬盘与分区基础知识

    我们是在虚拟机上安装的Linux系统.在安装的过程中,可能会遇到磁盘分区的问题,我们下面简单介绍一下分区的原理. 1.硬盘的基础知识 下面是一块空白的硬盘: 这是一块格式化后的硬盘: 格式化就是,在空 ...

  5. Java工作流框架jflow 集团应用模式用户组功能

    关键字 驰骋BPM ,工作流开发框架,用户组,接受人规则,用户组发起人范围,选择人范围. 集团工作模式. Ccflow ,jflow.工作流引擎 名词定义与应用背景 对于集团模式的ccflow,jfl ...

  6. Java 第十一届 蓝桥杯 省模拟赛十六进制转换成十进制

    问题描述 请问十六进制数1949对应的十进制数是多少?请特别注意给定的是十六进制,求的是十进制. 答案提交 这是一道结果填空的题,你只需要算出结果后提交即可.本题的结果为一个整数,在提交答案时只填写这 ...

  7. Java实现 蓝桥杯 算法训练 My Bad(暴力)

    试题 算法训练 My Bad 问题描述 一个逻辑电路将其输入通过不同的门映射到输出,在电路中没有回路.输入和输出是一个逻辑值的有序集合,逻辑值被表示为1和0.我们所考虑的电路由与门(and gate, ...

  8. Java实现 LeetCode 699 掉落的方块(线段树?)

    699. 掉落的方块 在无限长的数轴(即 x 轴)上,我们根据给定的顺序放置对应的正方形方块. 第 i 个掉落的方块(positions[i] = (left, side_length))是正方形,其 ...

  9. Java实现 蓝桥杯VIP 算法训练 水仙花数

    这道题有两个方法,第一个就相对来说通俗易懂 第二个可以用到Java的一些方法 public class 水仙花数1 { public static void main(String[] args) { ...

  10. Java实现 LeetCode 172 阶乘后的零

    172. 阶乘后的零 给定一个整数 n,返回 n! 结果尾数中零的数量. 示例 1: 输入: 3 输出: 0 解释: 3! = 6, 尾数中没有零. 示例 2: 输入: 5 输出: 1 解释: 5! ...