文章转自:http://www.cnblogs.com/wb-DarkHorse/archive/2013/04/27/3046749.html
一:# 、##和__VA_ARGS__
1、#
假如希望在字符串中包含参数,ANSI C是允许这样做的,在类函数宏的替换部分,#的作用相当于一个预处理的运算符,他可以把语言字符串转化成字符串。例如,如果x是一个宏变量,那么#x可以把参数名转化成相应的字符串。该过程成为字符串化(stringizing)。
#include <stdio.h>
#define PSQR(x) printf("the square of "#x" is %d.\n",(x)*(x))
int main(int argc,char *argv[]){
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(int argc,char *argv[]){
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的编译器不支持)。
实现思路就是宏定义中参数列表的最后一个参数为省略号(也就是三个点)。这样预定义宏__VA_ARGS__就可以被用在替换部分中,替换省略号所代表的字符串。比如:
#include <stdio.h>
#define PR(...) printf(__VA_ARGS__)
int main(int argc,char *argv[]){
int wt = 1,sp = 2;
PR("hello\n");
PR("weight = %d,shipping = %d\n",wt,sp);
return 0;
}
输出的结果是:
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:"__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 : toohoo
// 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;
}
测试结果为:
toohoo@ubuntu:~/myblog/C+CPP$ gcc hello.c -o hello
toohoo@ubuntu:~/myblog/C+CPP$ ./hello
File: hello.c, Line: 00020: A ha, check me: Hello World
关于#和##,还可以看看这篇博客: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;
总结:宏这个玩意是把双刃剑,因为编译器不会提供类型安全检查,只是进行替换,所以很容易将一些错误隐藏起来,总之,要在能把控的情况下玩弄,否则磨刀出鞘,会嗜血的!