page contents

C++中宏的使用

C/C++从编辑到生成目标文件的过程中,经历了预处理(#include、#if、#define)、编译、汇编和链接这几个过程,宏替换就是出现在预处理中,宏替换时不做任何的语法检查。

C/C++从编辑到生成目标文件的过程中,经历了预处理(#include、#if、#define)、编译、汇编和链接这几个过程,宏替换就是出现在预处理中,宏替换时不做任何的语法检查。由于宏替换时上下文可能存在不同的情况,因此要求对宏替换过程中对存在的变量使用括号包起来,切为了避免歧义,尽量不要在宏中使用自增自减运算符。

一、宏中使用#以及##
说明:“#”将宏中的参数字符串化,“##”将2个token连接为1个

// 输出class的大小#define OUT_CLASS_SIZE(_Class)  do{\
    cout << #_Class << " size is:" << sizeof(_Class) << endl;\
}while (0);// 获取基类的类型#define GetClassBase(type) C##type##Bassclass CHouseBass{};class CHouse:public CHouseBass{};// 以上定义了2个类,CHouse继承于CHouseBass,这里使用GetClassBase宏定义了一个基类的对象GetClassBase(House)  housebassObj;


二、宏在字符串中的使用

在使用C++调用SQLITE的demon中,将宏替换写入了一个字符串中,但运行的时候与预期不一致。
先看代码:

#define _VAR_ARG_20 (20)#define _VAR_ARG_40 (40)#define _VAR_ARG_SPEC   (20-30)char *str = "Target Pin[_VAR_ARG_20]:Value[_VAR_ARG_40]:Spec[_VAR_ARG_SPEC]";


编译无问题,运行的时候,str中的各个宏并没有被替换。第一反应是,宏是在预编译的时候进行替换的,不应该存在问题才对。后来仔细想想,char *实际上是一个存放在静态存储区里面的字符串,其实质是一个右值,不允许修改,且其创建是在预编译之前完成的,字符串中的宏被视为普通的字符串不会被替换。

如果要实现字符串的替换,可以使用 # 号连接一个字符串与宏。也可以先使用占位符替换宏,然后以格式化字符的方法使用宏替换占位符达到目的。

三、宏在语言国际化中的使用

//第一版本的语言国际化// 中文、英文enum _EN_LANGUAGE{eLanguage_Chinese = 0,eLanguage_English};
_EN_LANGUAGE g_enLanguage = eLanguage_English;#define defString(no,en,cn) string string_language_no(GetLanguage()?en:cn);//等价于: string string_language_1101("Welcome To Luran's Home \r\n");defString(1101,"Welcome To Luran's Home \r\n","欢迎来到卢然之家\r\n")#define GETSTRING(no)   string_language_no//第二版本的语言国际化:对比第一版本是定义一个全局变量,第二版本定义的是一个string的数组,//且当不知道上次语言设置时,可以使用 eLanguage_Last 来设置继承上次语言设置enum _EN_LANGUAGE{eLanguage_Chinese = 0,eLanguage_English,eLanguage_Last};
_EN_LANGUAGE g_enLanguage = eLanguage_English;#define SetLanguage(language)  do {\
    if(eLanguage_Last != language)\
        g_enLanguage = language ;\
}while(0)#define GetLanguage() g_enLanguage#define defString(no,en,cn) static string string_id_##no[eLanguage_Last] = {en,cn};#define GETSTRING(no)   string_id_##no[GetLanguage()]defString(1101,"Welcome To Luran's Home \r\n","欢迎来到卢然之家\r\n")int main()
{
    SetLanguage(eLanguage_Chinese);
    cout << GETSTRING(1101).c_str() << endl;
    SetLanguage(eLanguage_English);
    cout << GETSTRING(1101).c_str() << endl;
    getchar();    return 0;
}


四、获类取成员变量的偏移量

#define OFFSET(structure, member) ((int)&((structure*)0)->member);


典型例子是通过某一类的成员变量的地址来获取该类对象的地址,如linux内核中链表的实现。
五、使用do{….}while(0)
使用do{….}while(0) 将需要的代码包裹起来,成为一个独立的语法单元,可以避免多条语句的情况下报错

#define dosomething(x) x++;x*=2;if(bok)
  dosomething(x);  // dosomething扩展后,由于if和else之间存在多条语句,且无{}包裹,导致便宜错误else  dootherthing();


使用do{}while(0)可以很好的消除以上问题

#define dosome(x) do{\
    x++;\
    x*=2;\  
}while(0)


六、进行类型强转
以下代码将指定地址转换为class_ex类型的对象或该对象的指针

#define addr_to_class_ex( p_addr ) ( *( (class_ex*) (p_addr) ) )#define addr_to_class_ex( p_addr ) ( ( (class_ex*) (p_addr) ) )


七、防止溢出

#define INC_VAL( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val))


八、返回数组个数

#define ARRAY_NO(arr) ((sizeof(arr)/sizeof(arr[0])))


九、宏调试

_LINE_ /*(两个下划线),对应%d*/_FILE_ /*对应%s*/_DATE_ /*对应%s*/_TIME_ /*对应%s*/


十、宏陷阱

宏使用时由于不能进行类型检查,不能生成调试时的标识符,切存在一些其他副作用,如:

// 当a == b 时,结果将与预期不符
#define swap(a,b) do \
{\    a = a + b;\
    b = a - b;\
    a = a - b;\} while (0);// 当调用max时,如果使用了a++或者b++进行调用,则结果与预期不符#define max(a,b) ((a)>(b)?(a):(b))


  • 发表于 2021-05-18 19:41
  • 阅读 ( 511 )
  • 分类:C/C++开发

0 条评论

请先 登录 后评论
五福
五福

12 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1470 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章