C语言中的宏(整理)

文章转自: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;

总结:宏这个玩意是把双刃剑,因为编译器不会提供类型安全检查,只是进行替换,所以很容易将一些错误隐藏起来,总之,要在能把控的情况下玩弄,否则磨刀出鞘,会嗜血的!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值