前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C语言 宏嵌套的展开规则

C语言 宏嵌套的展开规则

作者头像
混说Linux
发布2023-02-24 16:13:20
1.4K0
发布2023-02-24 16:13:20
举报
文章被收录于专栏:混说Linux

第一时间看干货文章

1

C语言中,宏是在预编译时用宏体内容“文本替换”代码中的宏名的。

先讲一些宏嵌套的展开规则:

  1. 一般的展开规律像函数的参数一样:先展开参数,再分析函数,即由内向外展开;
  2. 当宏中有#运算符的时候,不展开参数;
  3. 当宏中有##运算符的时候,先展开函数,再分析参数;
  4. ##运算符用于将参数连接到一起,预处理过程把出现在##运算符两侧的参数合并成一个符号,注意不是字符串;

“#”和“##”的使用规则:

“#”是将宏参数转换为字符串。不管该参数宏什么,即“原貌”用字符串显示出来。即将宏参数用双引号“”包裹起来形成一个字符串。例如:

代码语言:javascript
复制
#define T(x) #x
int temp = 10;
cout << T(temp) << endl; //输出 temp 而不是 10
// T(temp) -> "temp"  (将宏参数用双引号包含起来形成一个字符串)

“##”被称为连接符(concatenation),把宏参数与之前的token(参数/字符串空格等)连接起来。例如:

代码语言:javascript
复制
#define T(x) x##[2]
int a[5] = {1,2,3,4,5};
cout << T(a) << endl; //输出 3 即 a[2]

宏的常见展开错误:

代码语言:javascript
复制
// 1. 宏参数在宏体中未加括号包裹起来
#define T(a)  a*10
int a = 1;
cout << T(a+1) << endl; //输出 11 而非20

// 2. 整个宏体内容未加括号包裹起来
#define T(x)  x+1
cout << 10*T(1) << endl; //输出 11 而非20

解决办法:

  1. 在宏定义中,将参数加上括号,这样在替换时保证括号内的表达式优先运算。
  2. 利用括号将整个宏定义的内容括起来,保证整个宏定义中的表达式优先运算。

宏嵌套:

宏嵌套是宏使用的难点,也是易错点。下面我将宏嵌套的展开规则用流程图来说明一下:

注意:上图中的 2 和 3 是条件或,只要满足一个条件就会进入流程 5。

下面举例说明:

代码语言:javascript
复制
// example 1
#include <cstdio>
#define TO_STRING2(x)  #x
#define TO_STRING1(x)  #x
#define TO_STRING(x)   TO_STRING1(x)

#define PARAM(x)       #x
#define ADDPARAM(x)    INT_##x
 
int main()
{
    const char *str = TO_STRING(PARAM(ADDPARAM(1)));
    printf("%s\n", str); //输出: "ADDPARAM(1)"
 
    str = TO_STRING2(PARAM(ADDPARAM(1)));
    printf("%s\n", str); //输出: PARAM(ADDPARAM(1)) 
 
    return 0;
}

上例中两个嵌套宏的展开流程如下:

代码语言:javascript
复制
TO_STRING(PARAM(ADDPARAM(1)))
-> 展开 PARAM:TO_STRING("ADDPARAM(1)")
-> 展开 TO_STRING:TO_STRING1("ADDPARAM(1)")
-> 展开 TO_STRING1:"\"ADDPARAM(1)\""
TO_STRING2(PARAM(ADDPARAM(1)))
-> 展开 TO_STRING2:"PARAM(ADDPARAM(1))"
代码语言:javascript
复制
// example 2
#include <cstdio>
#define TO_STRING2(x) a_##x
#define TO_STRING1(x) #x
#define TO_STRING(x)  TO_STRING1(x)

#define PARAM(x)    #x
#define ADDPARAM(x) INT_##x
 
int main()
{
    const char *str = TO_STRING(TO_STRING2(PARAM(ADDPARAM(1))));
    printf("%s\n", str); //输出: a_PARAM(INT_1)
    return 0;
}

上例中嵌套宏的展开流程如下:

代码语言:javascript
复制
TO_STRING(TO_STRING2(PARAM(ADDPARAM(1))))
-> 展开 TO_STRING2:TO_STRING(a_PARAM(ADDPARAM(1))) //注意此次展开后,PARAM宏名被破坏了,变成了a_PARAM不再是有效的宏名了
-> 展开 ADDPARAM:TO_STRING(a_PARAM(INT_1))
-> 展开 TO_STRING:TO_STRING1(a_PARAM(INT_1))
-> 展开 TO_STRING1:"a_PARAM(INT_1)"

注意:嵌套宏的展开规则与编译器有关,不同的编译器可能对同一个嵌套宏展开不同。

以上测试都是在 VS2010(x86)上。

原文:https://zhuanlan.zhihu.com/p/344240420

本文整理自网络,版权归原作者所有,如有侵权,请联系删除。

往期推荐

分享一个开源的QT的串口示波器

C语言回调函数,提升C技巧必备

用C语言实现状态机设计模式

stm32可以跑Linux操作系统吗?

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-12-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 混说Linux 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档