短小精悍的宏(整理篇)
2013-04-27 11:57 钱吉 阅读(3045) 评论(0) 编辑 收藏 举报c/c++里面常常需要定义一些宏,以提高编程效率和调试,同时使得代码结构简练。这里从网上查找了一下,并集中整理,方便以后使用。个别地方加了一点自己的注释(红色字体表示)。其实都是些小儿科的编程技巧了,请勿见笑,只是想方便像我这样的新手共同学习。
一:#、##和__VA_ARGS__
(转自:http://www.cnblogs.com/zhujudah/admin/EditPosts.aspx?opt=1)
1.#
假如希望在字符串中包含宏参数,ANSI C允许这样做,在类函数宏的替换部分,#符号用作一个预处理运算符,它可以把语言符号转化成字符串。例如,如果x是一个宏参量,那么#x可以把参数名转化成相应的字符串。该过程称为字符串化(stringizing).
#incldue <stdio.h> #define PSQR(x) printf("the square of" #x "is %d.\n",(x)*(x)) int main(void) { int y =4; PSQR(y); PSQR(2+4); return 0; }
输出结果:
the square of y is 16.
the square of 2+4 is 36.
第一次调用宏时使用"y"代替#x;第二次调用时用"2+4"代#x。
2.##
##运算符可以用于类函数宏的替换部分。另外,##还可以用于类对象宏的替换部分。这个运算符把两个语言符号组合成单个语言符号。例如:#define XNAME(n) x##n
这样宏调用:
XNAME(4)
展开后:
x4
程序:
#include <stdio.h> #define XNAME(n) x##n #define PXN(n) printf("x"#n" = %d\n",x##n) int main(void) { int XNAME(1)=12;//int x1=12; PXN(1);//printf("x1 = %d\n", x1); return 0; }
输出结果:x1=12
关于#和##补充一点,来自《C语言高级编程》
当宏参数是另一个宏的时候
需要注意的是凡宏定义里有用'#'或'##'的地方宏参数是不会再展开.
1, 非'#'和'##'的情况
#define TOW(2)
#define MUL(a,b) (a*b)
printf("%d*%d=%d\n", TOW, TOW, MUL(TOW,TOW));
这行的宏会被展开为:
printf("%d*%d=%d\n", (2), (2), ((2)*(2)));
MUL里的参数TOW会被展开为(2).
2, 当有'#'或'##'的时候
#define A(2)
#define CONS(a,b)int(a##e##b)
这行会被展开为:
printf(“%s\n”, CONS(A, A));// compile error
这一行则是:printf("%s\n", int(AeA));
INT_MAX和A都不会再被展开, 然而解决这个问题的方法很简单. 加多一层中间转换宏. 加这层宏的用意是把所有宏的参数在这层里全部展开, 那么在转换宏里的那一个宏(_STR)就能得到正确的宏参数.
#define A(2)
#define _CONS(a,b)int(a##e##b)
#define CONS(a,b)_CONS(a,b)// 转换宏
printf("%d\n", CONS(A, A));
输出为:200
CONS(A, A)-->_CONS((2), (2))--> int((2)e(2))
3.可变参数宏 ...和__VA_ARGS__
__VA_ARGS__ 是一个可变参数的宏,很少人知道这个宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持)(我用的vc2008也可以)。
实现思想就是宏定义中参数列表的最后一个参数为省略号(也就是三个点)。这样预定义宏__VA_ARGS__就可以被用在替换部分中,替换省略号所代表的字符串。比如:
#define PR(...) printf(__VA_ARGS__) int main() { int wt=1,sp=2; PR("hello\n"); PR("weight = %d, shipping = %d",wt,sp); return 0; }
(我的注释:__VA_ARGS__这个宏的头尾是两个下划线,包括后面提到的另外的一些编译器内置宏,不要搞错了哦。)
输出结果:
hello
weight = 1, shipping = 2
省略号只能代替最后面的宏参数。
#define W(x,...,y)错误!
(可变宏的另外一个在调试过程中很给力的用途,这篇文章里有个例子:http://blog.csdn.net/hinyunsin/article/details/6546670,现在整理如下,感谢原作者!)
编译器内置宏:
先介绍几个编译器内置的宏定义,这些宏定义不仅可以帮助我们完成跨平台的源码编写,灵活使用也可以巧妙地帮我们输出非常有用的调试信息。
ANSI C标准中有几个标准预定义宏(也是常用的):
__LINE__:在源代码中插入当前源代码行号;
__FILE__:在源文件中插入当前源文件名;
__DATE__:在源文件中插入当前的编译日期
__TIME__:在源文件中插入当前编译时间;
__STDC__:当要求程序严格遵循ANSI C标准时该标识被赋值为1;
__cplusplus:当编写C++程序时该标识符被定义。
编译器在进行源码编译的时候,会自动将这些宏替换为相应内容。
看到这里,你的眼睛应该一亮了吧,嗯,是的,__FILE__和__LINE__正是我们前面想要的输出的,于是,我们的每一条语句都变成了:
DEBUG("FILE: %s, LINE: %d…",__FILE__,__LINE__,…)
其实没有必要,__FILE__本身就会被编译器置换为字符常量,于是乎我们的语句又变成了这样:
DEBUG("FILE:"__FILE__", LINE: %d…",__LINE__,…)
但是,我们还是不满足,依然发现,还是很讨厌,为什么每条语句都要写"FILE:"__FILE__", LINE: %d 以及,__LINE,这两个部分呢?这不是浪费我们时间么?
哈哈,是的,这就是本次大结局,把DEBUG写成这样:
DEBUG(format,...) printf("FILE: "__FILE__", LINE: %d: "format"/n", __LINE__, ##__VA_ARGS__)
没错,就是这样!下面,所有的DEBUG信息都会按照这样的方式输出:
FILE: xxx, LINE: xxx, …….
最后:
最后,老规矩,coding测试。
//============================================================================ // Name : debug.cpp // Author : boyce // Version : 1.0 // Copyright : pku // Description : Hello World in C++, Ansi-style //============================================================================ #include <stdio.h> #define __DEBUG__ #ifdef __DEBUG__ #define DEBUG(format,...) printf("File: "__FILE__", Line: %05d: "format"\n", __LINE__, ##__VA_ARGS__) #else #define DEBUG(format,...) #endif int main(int argc, char **argv) { char str[]="Hello World"; DEBUG("A ha, check me: %s",str); return 0; }
输出结果:
(关于上面的DEBUG宏里面的__FILE__,__LINE__,我本以为可以这样改:
#define DEBUG(format,...) printf("File: "__FILE__", Line: "__LINE__", "format"\n", ##__VA_ARGS__)
即让编译器自动置换__LINE__宏,但是编译错误,我想可能是编译器不会像__FILE__宏一样,自己主动置换这个宏,而是需要你去格式化输出,所以这样也许会报错:
printf("File: "__FILE__", Line: "__LINE__"\n");
果然报错:error C2064: 项不会计算为接受 1 个参数的函数。所以用的时候还是老老实实按照格式化输出的形式写吧,呵呵,这个小问题在这里备注一下。
另外代码中的这种形式:#define CCLOG(format, ...) cocos2d::CCLog(format, ##__VA_ARGS__),' ## '的意思是,如果可变参数被忽略或为空,将使预处理器( preprocessor )去除掉它前面的那个逗号
关于#和##,还可以看看这篇博客:http://www.cnblogs.com/morewindows/archive/2011/08/18/2144112.html,讲的比较详细)
4 计算结构体中成员变量的偏移
#define offset(s,m) (size_t)&(((s*)0)->m)
二:C++中有用的一些宏定义
1,命名空间使用的宏定义
最近在看cocos2d-x的代码,有些地方写的很有意思(我自己以前代码写的很少,可能少见多怪了),所以记录下来。其实这也不算技巧了,对于做事讲究省布料的人,可以这么做:
#define NS_CC_BEGIN namespace cocos2d { #define NS_CC_END } #define USING_NS_CC using namespace cocos2d
用的时候,可以这样写:
/****custom head file***/ //**.h NS_CC_BEGIN //add your code here NS_CC_END //**.cpp #include "**.h" USING_NS_CC; //add your code here
2,成员变量和函数的定义
#define CC_PROPERTY_READONLY(varType, varName, funName)\ protected: varType varName;\ public: virtual varType get##funName(void); #define CC_PROPERTY_READONLY_PASS_BY_REF(varType, varName, funName)\ protected: varType varName;\ public: virtual const varType& get##funName(void);
再看一个:
#define CC_PROPERTY(varType, varName, funName)\ protected: varType varName;\ public: virtual varType get##funName(void);\ public: virtual void set##funName(varType var); #define CC_PROPERTY_PASS_BY_REF(varType, varName, funName)\ protected: varType varName;\ public: virtual const varType& get##funName(void);\ public: virtual void set##funName(const varType& var);
这样的宏定义是不是很奇葩?但很好懂,而且比较节省体力,方便为类声明一些成员,同时提供外部读或写接口。写在这里借鉴下。
三:常用宏
这个也是来自cocos2d-x的代码,在头文件"CCPlatformMacros.h"中。
#define CC_SAFE_DELETE(p) if(p) { delete (p); (p) = 0; } #define CC_SAFE_DELETE_ARRAY(p) if(p) { delete[] (p); (p) = 0; } #define CC_SAFE_FREE(p) if(p) { free(p); (p) = 0; } #define CC_SAFE_RELEASE(p) if(p) { (p)->release(); } #define CC_SAFE_RELEASE_NULL(p) if(p) { (p)->release(); (p) = 0; } #define CC_SAFE_RETAIN(p) if(p) { (p)->retain(); } #define CC_BREAK_IF(cond) if(cond) break;
总结:宏这个玩意是把双刃剑,因为编译器不会提供类型安全检查,只是进行替换,所以很容易将一些错误隐藏起来,总之,要在能把控的情况下玩弄,否则磨刀出鞘,会嗜血的!
暂时整理这几个,以后有其它的再添加进来。错误之处请大家使劲指点,我会像海绵一样吸纳!!