C/C++ 宏函数初级理解

C/C++ 宏魔法的初级理解

参考连接 https://zhuanlan.zhihu.com/p/152354031

简介

和模板元编程不一样,宏编程 没有类型 的概念,输入和输出都是 符号 —— 不涉及编译时的 C++ 语法,只进行编译前的 文本替换:

  • 一个宏参数 是一个任意的 符号序列 ,不同的宏参数之间用逗号分割
  • 每个参数都可以是空序列 而且空白字符会被忽略 (a + 1 == a+1)
  • 在参数中 , 尽量使用将每一个元素用()封装 以免编译器使用参数中的’,’ 把该参数划为两个参数
    • (例如#define FOO(bool, std::pair<int, int>) 被认为是 FOO() 有三个参数:bool / std::pair<int / int>)
    • 建议是写成FOO((bool),(std::pair<int,int>))
建议

让编译器 仅输出预处理结果
gcc -E 让编译器 在预处理结束后停止,不进行 编译、链接
gcc -P 屏蔽编译器 输出预处理结果的 行标记 (linemarker),减少干扰
另外,由于输出结果没有格式化,建议先传给 clang-format 格式化后再输出
屏蔽 无关的 头文件
临时删掉 不影响宏展开的 #include 行
避免多余的 引用展开,导致实际关注的宏代码 “被淹没”

初级初级用法

使用 ‘#+参数’ 将参数转化为字符串 或用 ## 将参数粘和在一起

#include<cstdio>
#include<climits>
using namespace std;

#define STR(s) #s
#define CONS(a,b) int(a##e##b) 
 
int main(int argc, char **argv)
{
    printf(STR(asdf));
    printf("%d",CONS(1,2));
    return 0;
}

第一句 STR(asdf) 在预处理阶段 转换为 “asdf” //即打印字符串 asdf
第二句 CONS(1,2) 在预处理阶段 转换为 1e2 整体相当于 printf(“%d”,1e2); 即打印 100

宏的参数是另一个宏的时候

!!! 注意 凡是宏定义参数里有"#“/”##"的地方宏参数是不会展开的

非"#“/”##"的情况

#define TOW (2)
#define MUL(a,b) (a*b)

printf("%d*%d=%d",TOW,TOW,MUL(TOW,TOW));

MUL()里的TOW是会被转换为(2)的
宏定义中无#时在源代码中是可以任意嵌套使用的

当有’#‘或’##'的时候

#define A (2) 
#define STR(s) #s
#define CONS(a,b) (a##e##b)
#include<climits>
//相当于在这里定义
//define INT_MAX  0x7fffffff

 在此 我们要拿到字符串形式的 "0x7fffffff" 
printf("int max: %s  ", STR(INT_MAX));              // INT_MAX #include<climits>  
        但拿到了"INT_MAX"    
       等于 printf("int max: %s  ", "INT_MAX");  

	 同理 这里我们要得到 "200"        
printf("%d  ", CONS(A, A)); // compile error   
        但得到了 int(AeA)
        等于	printf("%d  ", int(AeA));  

**

然而 为什么预编译的结果与预计不同?

**

符号拼接
  • 在宏编程中 , 符号拼接 通过 ## 将宏函数的参数 拼接为其他符号, 再进一步展开为目标结果, 这是实现宏编程的基础

然而 一个宏参数用于拼接标识符 那么他不会被展开 (例如上面的CONS(A,A))。

一种通用的方法是延迟拼接操作

#define _STR(s) #s
#define STR(s) _STR(s)
//define INT_MAX 0x7fffffff

#define A(2)
#define _CONS(a,b) int(a##e##b)
#define CONS(a,b) _CONS(a,b)

printf("int max: %s  ", STR(INT_MAX));  //-> printf("int max: %s", "0x7fffffff");
printf("%d  ", CONS(A, A));             //-> printf("%d  ",2e2);

底层原理:>

  • 在进入宏函数前,所有 宏参数 会先进行一次 预扫描 (prescan),完全展开未用于拼接标识符 或 获取字面量 的所有参数
  • 在宏函数展开时,用(预扫描展开后的)参数替换 展开目标里的 同名符号
  • 在宏函数展开后,替换后的文本会进行 二次扫描(scan twice),继续展开 结果里出现的宏
  • 所以,CONS()先展开参数,再传递给 _CONS() 进行 实际拼接
'#‘和’##'的一些应用特例
合并匿名变量名
#define ___ANONYMOUS1(type, var, line) type var##line  
#define __ANONYMOUS0(type, line) ___ANONYMOUS1(type, _anonymous, line)  
#define ANONYMOUS(type) __ANONYMOUS0(type, __LINE__)  

例:ANONYMOUS(static int); 即: static int _anonymous70; 70表示该行行号;
第一层:ANONYMOUS(static int); --> __ANONYMOUS0(static int,__LINE__);
第二层: --> ___ANONYMOUS1(static int, _anonymous, 70);
第三层: --> static int _anonymous70;
即每次只能解开当前层的宏,所以__LINE__在第二层才能被解开;

填充结构
#define FILL(a) {a, #a}  

enum IDD{OPEN, CLOSE};  
typedef struct MSG{  
  IDD id;  
  const char * msg;  
}MSG;  

MSG _msg[] = {FILL(OPEN), FILL(CLOSE)};  

这里的_msg[]相当于{{OPEN,“OPEN”},{CLOSE,“CLOSE”}};

得到一个数值类型所对应的字符串缓冲大小
#define _TYPE_BUF_SIZE(type) sizeof#type 
#define TYPE_BUF_SIZE(type) _TYPE_BUF_SIZE(type)

char buf[TYPE_BUF_SIZE(INT_MAX)];
// == char buf[11]

这里相当于
–> char buf[_TYPE_BUF_SIZE(0x7fffffff)];
–> char buf[sizeof “0x7fffffff”];
两步 , 声明了一个char buf[11];


DONE

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值