展开流程伪码:(自己总结的,不一定对)

//loop: 
//将实参代入文本中
//if 在某个实参之前有符号“#”(字符串化)或“##”(连接)then
//    对当前的文本做一次字符串化或者连接
//
//    if 结果是另外一个宏名 then
//        替换一次
//    end
//
//    结束当前的处理
//else
//    foreach 实参 do
//        if 是宏 then
//            goto loop
//        end
//    end
//    
//    if 文本中不包含任何宏 then
//        if 结果是另外一个宏名 then
//            替换一次
//        end
//
//        结束全部处理
//    else
//        goto loop
//    end
//end

示例:

#include <iostream>
using namespace std;

#define to_str(x) #x // 称【#x】为文本,称【x】为形参
#define macro_expansion(m) to_str(m)

#define token_cat(x, y) x##y
#define token_cat_ex(x, y) token_cat(x, y)

#define max(a, b) ((a) > (b) ? (a) : (b))

#define ab token_cat(1, 2)

int main(int argc, char* argv[])
{
    cout << to_str(token_cat(1, 2)) << endl; // token_cat(1, 2)
    // 代入实参=> #token_cat(1, 2)
    // 实参之前有符号##,对当前文本做一次连接,然后结束当前的处理=> "token_cat(1, 2)"
    
    cout << macro_expansion(to_str(token_cat(1, 2))) << endl; // "token_cat(1, 2)"
    // 代入实参=> to_str(to_str(token_cat(1, 2)))
    // 实参是宏,对实参做处理(这是当前处理),代入其实参=> to_str(#token_cat(1, 2))
    // 当前处理中,实参之前有符号#,对当前文本做一次字符串化,然后结束当前的处理=> to_str("token_cat(1, 2)")
    // 实参处理已完成,最外层还是一个宏,代入实参(注意当前处理变了)=> #"token_cat(1, 2)"
    // 实参之前有符号#,对当前文本做一次字符串化,然后结束当前的处理=> "\"token_cat(1, 2)\""

cout << macro_expansion(token_cat(token_cat(1, 2), 3)) << endl; // token_cat(1, 2)3
    // 代入实参=> to_str(token_cat(token_cat(1, 2), 3))
    // 实参是宏,对实参做处理(这是当前处理),代入其实参=> to_str(token_cat(1, 2)##3))
    // 当前处理中,实参之前有符号##,对当前文本做一次连接,然后结束当前的处理=> to_str(token_cat(1, 2)3))
    // 实参处理已完成,最外层还是一个宏,代入实参(注意当前处理变了)=> #token_cat(1, 2)3)
    // 实参之前有符号##,对当前文本做一次连接,然后结束当前的处理=> "token_cat(1, 2)3)"

cout << macro_expansion(token_cat_ex(token_cat_ex(1, 2), 3)) << endl; // 123
    // 代入实参=> to_str(token_cat_ex(token_cat_ex(1, 2), 3))
    // 实参是宏,对实参做处理(这是当前处理),代入其实参=> to_str(token_cat(token_cat_ex(1, 2), 3))
    // 第二次代入实参后,第一个实参仍然是宏,继续代入=> to_str(token_cat(token_cat(1, 2), 3))
    // 第一个实参仍然是宏,继续代入=> to_str(token_cat(1##2, 3))
    // 连接第一个实参,至此第一个实参处理完成=> to_str(token_cat(12, 3))
    // 后面的实参不是宏,为上层的宏代入实参=> to_str(12#3)
    // 连接一次=> to_str(123)
    // 全部实参处理完成,最外层还是一个宏,代入实参=> #123
    // 实参之前有符号#,对当前文本做一次字符串化,然后结束当前的处理=> "123"

int n = max(1, 2);
    // 代入实参=> ((1) > (2) ? (1) : (2))
    // 文本中不包含任何宏,结束全部处理

cout << macro_expansion(max(1, 2)) << endl; // ((1) > (2) ? (1) : (2))
    // 代入实参=> to_str(max(1, 2)) 
    // 实参是宏,对实参做处理(这是当前处理),代入其实参=> to_str(((1) > (2) ? (1) : (2))) 
    // 实参处理完了,文本中还包含一个宏,代入实参=> #((1) > (2) ? (1) : (2))
    // 实参之前有符号#,对当前文本做一次字符串化,然后结束当前的处理=> "((1) > (2) ? (1) : (2))"

cout << macro_expansion(token_cat(a, b)) << endl; // token_cat(1, 2)
    // 代入实参=> to_str(token_cat(a, b))
    // 实参是宏,对实参做处理(这是当前处理),代入其实参=> to_str(a##b)
    // 当前处理中,实参之前有符号##,对当前文本做一次连接,然后结束当前的处理=> to_str(ab)
    // ab是另外一个宏的名字,替换一次,注意只替换一次=> to_str(token_cat(1, 2))
    // 实参处理已完成,最外层还是一个宏,代入实参(注意当前处理变了)=> #token_cat(1, 2)
    // 实参之前有符号#,对当前文本做一次字符串化,然后结束当前的处理=> "token_cat(1, 2)"

return 0;
}

我自己的一些使用

#define TOKEN_CAT(x, y) x##y

//AUX_OSS_ACTORID(rstOssExploreSecret, ActorID, m_poPetOnDuty->GetExclusiveItemID());
// 参数:日志消息体,字段名,程序中的Actor ID对象
#define AUX_OSS_ACTORID(x, y, z)\
do {\
    x.TOKEN_CAT(y, _Sid) = z.uSid;\
    x.TOKEN_CAT(y, _ZoneID) = z.uZoneID;\
} while (0)

//AUX_OSS_THING_GUID(rstOssExploreSecret, PetGUID, m_poPetOnDuty->GetExclusiveItemID());
// 参数:日志消息体,字段名,程序中的GUID对象
#define AUX_OSS_THING_GUID(x, y, z) \
do {\
    unsigned int* apu32GUIDField[4] = {\
        &TOKEN_CAT(TOKEN_CAT(x, .), TOKEN_CAT(y, _Time)),\
        &TOKEN_CAT(TOKEN_CAT(x, .), TOKEN_CAT(y, _Reserve)),\
        &TOKEN_CAT(TOKEN_CAT(x, .), TOKEN_CAT(y, _Seq)),\
        &TOKEN_CAT(TOKEN_CAT(x, .), TOKEN_CAT(y, _SrvID))\
    };\
    *apu32GUIDField[0] = (z).dwTime;\
    *apu32GUIDField[1] = (z).dwReserve;\
    *apu32GUIDField[2] = (z).dwSeq;\
    *apu32GUIDField[3] = (z).dwSrvID;\
} while (0)

struct TRewardGroup
{
    int m_i32RewardGroupID;
    float m_f32Prop;
    int m_i32BindMark;
    int m_i32OutputMode;
};

#define REWARD_GROUP(_pstCfg, i) {TOKEN_CAT(TOKEN_CAT(_pstCfg, ->), TOKEN_CAT(iRewardGroupID, i)),\
    TOKEN_CAT(TOKEN_CAT(_pstCfg, ->), TOKEN_CAT(iProp, i)) / reward_group_rate_base, \
    TOKEN_CAT(TOKEN_CAT(_pstCfg, ->), TOKEN_CAT(iBindMark, i)), \
    TOKEN_CAT(TOKEN_CAT(_pstCfg, ->), TOKEN_CAT(iOutputMode, i))}

TRewardGroup astDynamicRewardGroup[] = {
    REWARD_GROUP(pstDynamicOutputGoods, 1), REWARD_GROUP(pstDynamicOutputGoods, 2), 
    REWARD_GROUP(pstDynamicOutputGoods, 3), REWARD_GROUP(pstDynamicOutputGoods, 4), 
    REWARD_GROUP(pstDynamicOutputGoods, 5), REWARD_GROUP(pstDynamicOutputGoods, 6), 
    REWARD_GROUP(pstDynamicOutputGoods, 7), REWARD_GROUP(pstDynamicOutputGoods, 8),
    REWARD_GROUP(pstDynamicOutputGoods, 9), REWARD_GROUP(pstDynamicOutputGoods, 10)
};

struct TRewardGoods
{
    int m_i32RewardItemID;
    int m_i32RewardItemCount;
    int m_i32RewardItemBind;
};

#define REWARD_GOODS(i) {pstRewardCfg->TOKEN_CAT(iRewardItemID, i), \
    pstRewardCfg->TOKEN_CAT(iRewardItemCount, i), \
    pstRewardCfg->TOKEN_CAT(iRewardItemBind, i)}

TRewardGoods astRewardGoods[] = {
    REWARD_GOODS(1), REWARD_GOODS(2), REWARD_GOODS(3)
};

下面一篇文章http://www.boost.org/doc/libs/1_37_0/libs/wave/doc/macro_expansion_process.html

The Macro Expansion Process

The macro expansion process described here was initially developed by Paul Mensonides and is implemented in Wave. It is much more understandable as the description of the desired macro expansion algorithm provided in the C++ Standard [1].

Macro replacement proceeds left-to-right.

If, during scanning (or rescanning) an identifier is found, it is looked up in the symbol table. If the identifier is not found in the symbol table, it is not a macro and scanning continues.

If the identifier is found, the value of a flag associated with the identifier is used to determine if the identifier is available for expansion. If it is not, the specific token (i.e. the specific instance of the identifier) is marked as disabled and is not expanded. If the identifier is available for expansion, the value of a different flag associated with the identifier in the symbol table is used to determine if the identifier is an object-like or function-like macro. If it is an object-like macro, it is expanded. If it is a function-like macro, it is only expanded if the next token is an left parenthesis.
An identifier is available for expansion if it is not marked as disabled and if the the value of the flag associated with the identifier is not set, which is used to determine if the identifier is available for expansion.

(If a macro is an object-like macro, skip past the next two paragraphs.)

If a macro to be expanded is a function-like macro, it must have the exact number of actual arguments as the number of formal parameters required by the definition of the macro. Each argument is recursively scanned and expanded. Each parameter name found in the replacement list is replaced by the expanded actual argument after leading and trailing whitespace and all placeholder tokens are removed unless the parameter name immediately follows the stringizing operator ('#') or is adjacent to the token-pasting operator ('##').

If the parameter name immediately follows the stringizing operator ('#'), a stringized version of the unexpanded actual argument is inserted. If the parameter name is adjacent to the token-pasting operator ('##'), the unexpanded actual argument is inserted after all placeholder tokens are removed.

All concatenation takes place in the replacement list. (If a single concatenation yields multiple tokens, the behavior is undefined. Moreover, Wave in normal C++98 and C99 modes issues an error, if more then one token is produced as the result of the concatenation. In C++0x mode Wave treats token-pasting of unrelated tokens as well defined and inserts the reparsed string representation of the concatenated tokens into the replacement list.).

The flag in the symbol table entry associated with the name of the macro being expanded is set to indicate the that the macro is not available for expansion.

The replacement list is rescanned for further macro expansion. All leading and trailing whitespace tokens in the replacement list are removed (the placeholder tokens are left intact).

After rescanning completes, the flag in the symbol table entry associated with the name of macro being expanded is cleared to indicate that the macro is again available for expansion, and the sequence of tokens that constitutes the rescanned replacement list is returned to the point of invocation of the macro.

If this sequence of tokens is empty, it is replaced by a placeholder token. If a placeholder is found during scanning (or rescanning) it is ignored. (Also, if the only thing separating a parameter from the stringizing operator or token-pasting operator is placeholder, it is also ignored in that context.)

This sequence of tokens is inserted at the original point that the macro was invoked, and scanning continues starting with the last token of the newly inserted sequence of tokens. I.e. scanning looks back a single token (possibly a placeholder token) and continues.

C语言中的宏是怎么展开的?相关推荐

  1. C语言余数为0输出intact,C语言中的宏是怎么展开的?

    展开流程伪码:(自己总结的,不一定对) //loop: //将实参代入文本中 //if 在某个实参之前有符号"#"(字符串化)或"##"(连接)then // ...

  2. C语言使用define定义圆周率,C语言中的宏处理

    在C语言中使用宏,我们经常这么做,但是为什么使用宏,他可以做什么,我们或许只是一知半解,下面简单介绍如何在C语言中使用宏预处理器. #define语句 对于define语句,我们已经非常熟悉,一个宏定 ...

  3. 关于C语言中的宏的一点点讨论

    前言: 熟悉C语言的朋友应该对宏不陌生, 宏在C语言程序开发中是经常使用的,使用其的主要目的是方便程序员的编程工作,并且能在一定程度上提高程序的效率.C语言中提供的宏定义命令是#define.下面就使 ...

  4. C语言怎么判断字符YN,c语言中的宏_详解(转)

    1. 简单宏定义 简单的宏定义有如下格式: [#define指令(简单的宏)] #define 标识符替换列表 替换列表是一系列的C语言记号,包括标识符.关键字.数.字符常量.字符串字面量.运算符和标 ...

  5. C语言中的宏函数与宏定义

    目录 1.无参宏定义 1.1 无参数宏定义的格式: 1.2 使用说明: 2.带参宏定义 2.1 带参数宏定义的格式: 2.2 使用说明: 3.带参宏定义与函数调用的区别 4.头文件中常用的宏定义 5. ...

  6. 一招让你彻底掌握C语言中运用宏以及#与##的妙用

    学习C语言,特别是阅读linux源码的时候,大家经常遇到很多的宏定义,有简单的,当然也有很复杂的. 有事一个宏定义甚至有几十行之多,遇到这种宏定义的大家基本上是一脸懵逼,不知所措,其实想复杂的宏定义没 ...

  7. c语言中关于宏和内联说法正确的是, 2011年1月高等教育自学考试全国统一命题考试 C++程序设计试题...

    版权声明:以上文章中所选用的图片及文字来源于网络以及用户投稿,由于未联系到知识产权人或未发现有关知识产权的登记,如有知识产权人并不愿意我们使用,如果有侵权请立即联系:55525090@qq.com,我 ...

  8. c语言宏函数怎么传递宏参数_C语言中的宏参数评估

    c语言宏函数怎么传递宏参数 We can define a function like Macro, in which we can pass the arguments. When a Macro ...

  9. C语言中关于宏定义的学习

    1.C语言中宏定义的使用 2.GCC官方文档 3.C语言宏定义的几个坑和特殊用法 转载于:https://www.cnblogs.com/Brandon0807/p/11146344.html

最新文章

  1. 用clock()统计代码的执行时间(C语言)
  2. TEMPO研究第一年影像学数据: 骨侵蚀修复几乎只出现在无关节肿胀或肿胀改善组...
  3. 为什么美团打车、滴滴外卖必败?君智谢伟山揭秘了背后的竞争战略逻辑
  4. 程序员/设计师能用上的 75 份速查表
  5. loopback接口、router ID详解
  6. 测试笔的使用_宽带故障怎么办?毕亚兹红光笔1秒定位光纤故障,快速解决问题...
  7. 前端面试题—2021年web前端开发面试题
  8. 小偷涂鸦 java_用Java做一个涂鸦板
  9. kubeadm部署k8s集群
  10. thinkphp5-php think常用命令
  11. android6.0获取通讯录权限
  12. zzzfun网站连接不上服务器,ZzzFun
  13. 概率论基础(sigma域、测度)
  14. dubbo之使用nacos作为注册中心
  15. 电脑网页打不开提示错误err connection怎么办?
  16. 火箭军计算机网络技术就业方向,计算机系统结构专业就业方向
  17. fcpx插件:stupid raisins info pop for mac(27个标题字幕栏)
  18. 魔兽服务器排队微信,服务器排队严重:《魔兽世界》经典怀旧服执行47服免费角色转移计划...
  19. 德州学院计算机系吧,任传成(计算机系)老师 - 德州学院 - 院校大全
  20. C++实验3-定期存款利息计算器

热门文章

  1. Google 的怪异域名大全
  2. 首页仪表盘echart_封装万能表单组件
  3. C/C++语言的服务器LS调研 (Language Server 实现代码索引 跳转定义 智能提示等功能)
  4. 硅晶圆短缺:12寸硅晶圆产能虽被包下,但交货困难!
  5. NKOJ P9669 Luogu P3951 小凯的疑惑 Plus
  6. 小凯的疑惑 原题+Plus 证明
  7. 彻底卸载和清除 老旧 MS OFFICE
  8. g4600黑苹果efi_普通电脑也能安装苹果系统,喜欢折腾就用黑苹果
  9. [渝粤教育] 盐城工学院 机电传动控制 参考 资料
  10. CSS进阶篇——展示 (display)