宏使用之预处理操作符总结

1.如何查看宏展开

      在开始之前,先简单的介绍下,我们自己定义的一个宏,在引用的代码中是如何被展开的。我用的是Microsoft Visual C++ 6.0,project menu -> settings,C/C++选项卡,在Project Options加上/P,如图 1 1所示。rebuild all,会产生与.cpp同名的.i文件。打开.i文件,在这里就可以看到代码中宏是如何被展开的。


图 1 1 添加“/P”编译选项


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

2.预处理操作符

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

Preprocessor OperatorsAction
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.总结

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

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值