Chinaunix首页 | 论坛 | 博客
  • 博客访问: 815343
  • 博文数量: 157
  • 博客积分: 542
  • 博客等级: 中士
  • 技术积分: 1696
  • 用 户 组: 普通用户
  • 注册时间: 2011-11-21 20:21
文章分类
文章存档

2017年(1)

2016年(2)

2015年(6)

2014年(42)

2013年(77)

2012年(19)

2011年(10)

分类: C/C++

2012-05-31 16:47:17

1.如何查看宏展开
      在开始之前,先简单的介绍下,我们自己定义的一个宏,在引用的代码中是如何被展开的。我用的是Microsoft Visual C++ 6.0,project menu -> settings,C/C++选项卡,在Project Options加上/P,



比如:
#define CONS(a,b)    int(a##e##b)
int n = CONS(2, 3);    // 被展开为int n = int(2e3);即n = 2000;

2.预处理操作符
      以前,我经常看到一些巧妙的宏定义,里面有用到#、##、等符号,但不懂这些#是干么用的,今天突然想起这事,决定把它们彻底搞明白。
      打开MSDN查看相关说明,如下表格所示,注意这些预处理操作符只能用在#define的宏定义中 。

Preprocessor Operators        Action
Stringizing operator (#)        字符串化操作符,给对应的实参加上双引号。
Charizing operator (#@)        字符化操作符,给对应的实参加上单引号。
Token-pasting operator (##)        符号连接操作符,串联两个符号。
2.1. Stringizing operator (#)
      字符串化操作符,作用:将宏定义中的传入参数名转换成用一对双引号括起来,即字符串化。我们测试看看(示例来自MSDN)。
#define stringer( x ) printf( #x "/n" )
void main()
{
    stringer( In quotes in the printf function call/n );
    stringer( "In quotes when printed to the screen"/n );   
    stringer( "This: /"  prints an escaped double quote" );
}
      用上面介绍的方法查看宏展开(在.i文件中),以上代码预编译时展开成如下代码,可以发现用字符串化(Stringizing)操作时,如果实参中有引号、反斜线字符时,宏展开时会在这些符号钱自动加上“/”以便将这些字符也字符串化。
void main()
{
    printf( "In quotes in the printf function call/n" "/n" );
    printf( "/"In quotes when printed to the screen/"/n" "/n" );   
    printf( "/"This: ///"  prints an escaped double quote/"" "/n" );
}
最终输入如下:
In quotes in the printf function call

"In quotes when printed to the screen"

"This: /"  prints an escaped double quote"
注意:
◆转义字符
◇某些形式的传入参数名中,若存在特殊字符,编译器会自动为其添加转义字符号'/'。

◆对空格的处理
◇忽略传入参数名前面和后面的空格。
如:stringer(   ABC   );   // 宏展开为printf( "ABC" "/n" );
◇当传入参数名间存在空格时,编译器将会自动连接各个子字符串,用每个子字符串中只以一个空格连接,忽略其中多余一个的空格。
如:stringer( ABC   DEF ); // 宏展开为printf( "ABC DEF" "/n" );

2.2. Charizing operator (#@)
      字符化操作符,作用:将宏定义中的传入参数名转换成用一对单引号括起来,即字符化。如:

#define MAKECHAR(ch)  #@ch

char a = MAKECHAR (b);  // 被宏展开为char a = 'b';
注意:
在默认的类型转换中,传入的参数如果不是字符型,会被截断成char型。
如:
char a = MAKECHAR (abcd); 将会截断成  a='d'。

2.3. Token-pasting operator (##)
      符号连接操作符,作用:将两个符号串联成一个符号使用。Token-Pasting Operator也成为merging operator,即合并操作。TEXT这个宏我想搞Windows编程的人都不陌生,我们看看它的定义:
#ifdef  UNICODE
#define __TEXT(quote) L##quote
#else
#define __TEXT(quote) quote
#endif

#define TEXT(quote) __TEXT(quote)
    用实际例子看看怎么个连接法,在UNICODE环境中:
TEXT(ABC);  // 宏展开为LABC;
TEXT("ABC"); // 宏展开为L"ABC";
    再比如:
#define CONS(a,b)    int(a##e##b)
int n = CONS(2, 3);    // 被展开为int n = int(2e3);即n = 2000;

3.宏嵌套
      宏定义是允许嵌套的,但要注意:宏定义里有用’#'、’#@’、'##'的地方宏参数是不会再展开 。为了说明问题,让我们来看看一个简单例子。

#define MAX_VAL 100
#define _STR(arg)  #arg
#define STR(arg)   _STR(arg)

int main()
{
    puts(_STR(MAX_VAL));
    puts(STR(MAX_VAL));
    return 0 ;
}
      先看看.i文件中的宏展开是如何的:
int main()
{
    puts("MAX_VAL");
    puts("100");
    return 0 ;
}
输出:
MAX_VAL
100
      从输出结果就可以看出其差别所在,之所以会这样,就是因为:宏定义里有用’#'、’#@’、'##'的地方宏参数是不会再展开 。然而解决这个问题的方法很简单,加多一层中间转换宏,中间转换宏的作用就是对宏参数进行宏展开。在许多大型项目、MFC代码、微软代码的头文件中这样的定义随处可见。就像上面介绍的TEXT宏嵌套定义亦是如此。

4.总结
      宏的设计很巧妙,只要你能想得出,能大大的强化你的代码,在学习和工作的过程中,要善于总结、归纳、收藏巧妙的宏应用。仅供参考,如有误,请赐教。如果我写的东西对你有用,不要忘了顶下。

阅读(1973) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~