您的位置:首页 > 其它

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

2010-04-28 20:11 148 查看

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

宏的设计很巧妙,只要你能想得出,能大大的强化你的代码,在学习和工作的过程中,要善于总结、归纳、收藏巧妙的宏应用。仅供参考,如有误,请赐教。如果我写的东西对你有用,不要忘了顶下。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: