宏的使用
前言
宏在C/C++中有挺大的作用。
关键词
宏的几种作用
1,定义常量
2,定义表达式
3,简化繁琐的代码
4,作为标识符
5,可变参数
6,#和##的使用
1,定义常量
比如定义PI的值可以通过宏来定义
#define PI    3.1415927
2,定义表达式
#define MAX(a, b)    (a>b?a:b)
3,简化繁琐的代码
这个可以简化一些重复的代码,比如声明函数,定义类的成员变量,或者是简化多次编写重复性高的代码。
简化函数声明,在函数声明的时候有些必要的关键字需要写,但是很多时候都是一样的,可以通过宏来简化
定义线程函数
unsigned __stdcall ThreadFunc(void* pArguments)
可以通过宏简化为
#define THREAD_FUNC(func) unsigned __stdcall func(void* pArguments) THREAD_FUNC(ThreadFunc) { printf("hello\n"); return 0; }
定义类的成员变量,可能需要定义成员变量的get,set函数,这时候可以通过宏来简化这个过程。
#define PROP_DECL(ClassName, Prop) \ public: ClassName Get##Prop(void){return m_##Prop;}\ public: void Set##Prop(ClassName vl){m_##Prop = vl;}\ private: ClassName m_##Prop; class CTestObj { PROP_DECL(int, Count) public: CTestObj(); ~CTestObj(); };
上面的代码经过预编译之后就会产生GetCount和SetCount两个函数和m_Count私有成员变量。
简化繁琐代码,在内存释放的时候可能需要把指针,需要两行代码
WA *pa = new WA(c); delete pa; pa = NULL;
可以使用宏来简化这个过程
#define MEM_FREE(x) do \ {\ delete x;\ x = NULL;\ } while (0) WA *pa = new WA(c); MEM_FREE(pa);
在ATL,MFC中大量使用到宏来简化代码。
4,作为标识
作为标识的宏在大量的使用
#ifndef __TMP_H__ //判断是否已经定义宏,如果没有,将会执行下面代码,用于避免包含文件的时候重复包含 #define __TMP_H__ //定义宏,这样第二次包含这个头文件的时候就不会执行下面的定义 //判断是否是UNICODE,用于定义TTCHAR的字符类型。 #ifdef UNICODE typedef wchar_t TTCHAR; #else typedef char TTCHAR; #endif //用于标识的宏定义 #define IN #define OUT #endif // !__TMP_H__
4,可变参数
宏可以有参数,而且参数数量可以不定
#define LOG(format, ...) printf(format, __VA_ARGS__) LOG("hello, %d, %s\n", 10, "nihao");
5,#和##的使用
#的作用是把宏参数变成字符串
#define STR(s) #s printf(STR(hello)); // 输出字符串"hello"
##的作用是把宏参数粘贴起来例子可以参考一下第三点的代码。
使用宏需要注意的点
宏是在预处理过程中进行存文本替换,预处理过程中不会对宏进行任何的语法检测,却个括号,少个分号都不会管,所以可能会出现一些很奇怪的错误,所以要慎用。
1,算法优先问题
一个乘法的宏
#define MUL(x, y)    (x * y)
MUL(2,3)的时候没有问题,如果是MUL(1+2, 3)的时候就出事了,宏会替换成1+2*3,跟预想的结果就不一样了。
这时候就需要把宏定义改成
#define MUL(x, y) (x) * (y)
2,分号吞噬问题
#define SKIP_SPACES(p, limit) \ { char *lim = (limit); \ while (p < lim) { \ if (*p++ != ' ') { \ p--; break; }}}
如果上面的代码放在判断语句中使用
if (*p != 0) SKIP_SPACES (p, lim); else ...
编译器会报错,else之前要有if,可以通过下面代码来解决
#define SKIP_SPACES(p, limit) \ do { char *lim = (limit); \ while (p < lim) { \ if (*p++ != ' ') { \ p--; break; }}} \ while (0)
这种方式在linux中大量使用到
3,重复调用
#define min(X, Y)  ((X) < (Y) ? (X) : (Y))
如果调用时这样
int x = 1; int y = 2; min(x++, y);
展开后x++会被调用两次,这种方式要避免。
4,对自身的递归引用
有如下宏定义:
#define foo (4 + foo)
按前面的理解,(4 + foo)会展开成(4 + (4 + foo),然后一直展开下去,直至内存耗尽。但是,预处理器采取的策略是只展开一次。也就是说,foo只会展开成(4 + foo),而展开之后foo的含义就要根据上下文来确定了。
对于以下的交叉引用,宏体也只会展开一次。
  
#define x (4 + y) #define y (2 * x)
x展开成(4 + y) -> (4 + (2 * x)),y展开成(2 * x) -> (2 * (4 + y))。
注意,这是极不推荐的写法,程序可读性极差。
5,宏参数预处理
宏参数中若包含另外的宏,那么宏参数在被代入到宏体之前会做一次完全的展开,除非宏体中含有#或##。
有如下宏定义:
#define AFTERX(x) X_ ## x #define XAFTERX(x) AFTERX(x) #define TABLESIZE 1024 #define BUFSIZE TABLESIZE
AFTERX(BUFSIZE)会被展开成X_BUFSIZE。因为宏体中含有##,宏参数直接代入宏体。
  
XAFTERX(BUFSIZE)会被展开成X_1024。因为XAFTERX(x)的宏体是AFTERX(x),并没有#或##,所以BUFSIZE在代入前会被完全展开成1024,然后才代入宏体,变成X_1024。
宏实在预处理过程中被替换掉的,所以在实际的编译过程中,不会出现宏,或者宏的变量名。
参考:
http://hbprotoss.github.io/posts/cyu-yan-hong-de-te-shu-yong-fa-he-ji-ge-keng.html
                    
                
                
            
        
浙公网安备 33010602011771号