#define 的神奇操作
# define 的神奇操作
一、宏定义中的 #、## 符号的神奇用法
1.1 # 的用法
1.1.1 作用
#
表示字符串化操作符(stringification),其作用是将宏定义中的传入参数名转换成用双引号括起来的参数名字符串。
现在对这句话是不是还不甚理解,没关系,让我们接着往下走。
1.1.2 举例说明
#include <stdio.h>
#define ToString(str) #str
int main()
{
char str[] = ToString(hello world); // 等价于 "hello world";
}
根据例子,我们再来理解一下#
的作用:将宏定义(ToString)中的传入参数名(hello world)转化成用双引号括起来的参数名字符串("hello world")。
是不是有种恍然大明白的感觉~
1.1.3 实战
假设给你一个 JSON 字符串:
{"name" : "张三"}
如何以 C 语言的形式输出呢?
你可以这么做char str[] = "{\"name\":\"张三\"}";
,通过使用转义字符\"
使得代码可以保存引号"
。
既然学习了宏定义中#
的作用,那么我们通过#
修改一下:
#define ToString(str) #str
int main()
{
char str[] = ToString({"name":"张三"});
}
好处就是不用添加\
,阅读起来更直观。
1.2 ## 的用法
1.2.1 作用
##
表示连接,将宏定义中的一个或多个形参转换成一个实际的参数名。
1.2.2 举例说明
老规矩,先上代码再解释:
#include <stdio.h>
#define Conn(x, y) x##y
int main()
{
int num1 = 10;
int n = Conn(num, 1); // n = num1
return 0;
}
根据例子,我们再来理解一下##
的作用:将宏定义(Conn)中的多个形参(num 和 1)转换成一个实际的参数名(num1)。
或者你也可以这么写:
#include <stdio.h>
#define Conn(x) num##x
int main()
{
int num1 = 10;
int n = Conn(1); // n = num1
return 0;
}
将宏定义(Conn)中的一个形参(1)转化为实际的参数名(num1)。
1.2.3 错误用法
通过上面的举例,你现在是不是已经明白了##
的作用了,那么来分析一下下述代码的输出结果:
#include <stdio.h>
#define Conn(x) num##x
int main()
{
int num1 = 1;
int num2 = 2;
int num3 = 3;
int numi = 100;
for (int i = 1; i <= 3; i++)
{
int num = Conn(i);
printf("num = %d\n", num);
}
return 0;
}
期望的输出结果是不是:
- num = 1
- num = 2
- num = 3
下面让我们看一下实际运行结果:
是不是很意外?Conn 宏不是起连接作用吗为什么输出的结果不是预期呢?
还记得 gcc 的 -E 指令吗,让我们通过 -E 看一下 gcc 预编译的文件:
下面让我们来分析一下 main.i 文件内容:
int main()
{
int num1 = 1;
int num2 = 2;
int num3 = 3;
int numi = 100;
for (int i = 1; i <= 3; i++)
{
int num = numi;
printf("num = %d\n", num);
}
return 0;
}
是不是找到问题所在了。原因在于Conn(i)
并没有按照我们的预期依次替换为 num1、num2、num3,而是替换为了 numi。
将宏 Conn(i) 中 i 的值替换为其实际的整数值是不可能的,这是因为宏替换发生在代码编译之前,也就是说宏替换的时候并不知道 i 是一个 int 型的变量,仅仅将其当做一个字符处理。
解释参考自问题评论:将变量的值传递给C中的宏 - 或代码 (orcode.com)
二、实际使用
最近在看 ONVIF 代码的时候,看到一个神奇的操作,下面是简化版本:
#include <stdio.h>
/* 函数声明 */
#define DECLARE(x) \
void Func_##x(int a);
/* 函数定义 */
#define DEFINE(x) \
void Func_##x(int a) \
{ \
printf("a 的值为 %d, %s 型\n", a, #x); \
}
/* 函数创建 */
#define CREATE(x) \
Func_##x
DECLARE(int)
DEFINE(int)
int main()
{
CREATE(int)(10);
return 0;
}
在学习了#
和 ##
的用法后,上面的代码就迎刃而解了,转化成普通的代码为:
#include <stdio.h>
void Func_int(int a); // 声明函数 --- DECLARE(int)
void Func_int(int a) // 定义函数 --- DEFINE(int)
{
printf("a 的值为 %d, %s 型\n", a, "int");
}
int main()
{
Func_int(10); // 使用函数 --- CREATE(int)(10)
return 0;
}
参考资料
- #define宏定义中的#,##,@#,\ 这些符号的神奇用法 - 简书 (jianshu.com)
- C语言宏#define中#,##,#@和\的用法_yishizuofei的博客-CSDN博客
- #define用法集锦(非常全)_king110108的博客-CSDN博客_#define (...)
最新文章
- discuzX3后台管理插件开发示例一 用户表查询
- 如何发布得到.ipa文件
- IOS GCD 浅析
- iOS高级必备
- insertAdjacentHTML和insertAdjacentText方法
- 【Tsinghua OJ】范围查询(Range)问题
- Android--创建进度框ProgressDialog
- USACO Section 2.2: Subset Sums
- hdoj 1596 find the safest road
- 转载crontab例行工作调度
- spring.net 和 mybatis.net
- 封装好的PHP分页类,简单好用--在开源看到的,取回来自己用
- 乱译文档--Musca介绍
- mongodb数据文件内部结构
- UVA 10574 - Counting Rectangles(枚举+计数)
- java基础(一)面向对象
- [SCOI 2010]字符串
- 磁盘IOPS计算与测量
- ActiveMQ与RabbitMQ的区别
- 利用ASK/OOK 发射模块,实现信号重放
热门文章
- Docker 部署 RocketMQ Dledger 集群模式( 版本v4.7.0)
- logstash另类输出到es
- 第二章:视图层 - 6:QueryDict对象
- 为什么中国的To B企业都活的很“惨”?
- 「Tubian」Tubian0.4!完全开源!
- ASP.NET Core 中的模型绑定
- Docker安装MongoDB并使用Navicat连接
- 开源数字基础设施 项目 -- Speckle
- Python学习笔记----列表、元组和字典的基础操作
- Codeforces Round #553 (Div. 2)/codeforces1151