下载本文示例代码
在上一篇文章中,我演示了几个常用的宏定义和预处理指令,但可以说这些都是相当常规的技巧。下面要介绍的宏定义与预处理指令的用法也是ATL,MFC以及LINUX中使用得比较多的非常重要的技巧。 ## 连接符与# 符 ## 连接符号由两个井号组成,其功能是在带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串。但它不可以是第一个或者最后一个子串。所谓的子串(token)就是指编译器能够识别的最小语法单元。具体的定义在编译原理里有详尽的解释,但不知道也无所谓。同时值得注意的是#符是把传递过来的参数当成字符串进行替代。下面来看看它们是怎样工作的。这是MSDN上的一个例子。 假设程序中已经定义了这样一个带参数的宏:#define paster( n ) printf( "token" #n " = %d", token##n ) 同时又定义了一个整形变量:int token9 = 9; 现在在主程序中以下面的方式调用这个宏:paster( 9 ); 那么在编译时,上面的这句话被扩展为:printf( "token" "9" " = %d", token9 ); 注意到在这个例子中,paster(9);中的这个”9”被原封不动的当成了一个字符串,与”token”连接在了一起,从而成为了token9。而#n也被”9”所替代。 可想而知,上面程序运行的结果就是在屏幕上打印出token9=9 在ATL的编程中,我们查看它的源代码就会经常看见这样的一段:#define IMPLEMENTS_INTERFACE(Itf) \{&IID_##Itf, ENTRY_IS_OFFSET,BASE_OFFSET(_ITCls, Itf) }, 我们经常不假思索的这样使用它:……IMPLEMENTS_INTERFACE(ICat)…… 实际上IID_ICat 已经在别的地方由ATL向导定义了。当没有向导的时候,你只要遵循把IID_加在你的接口名前面来定义GUID的规则就也可以使用这个宏。在实际的开发过程中可能很少用到这种技巧,但是ATL使用得如此广泛,而其中又出现了不少这样的源代码,所以明白它是怎么一回事也是相当重要的。我的一个朋友就是因为不知道IMPLEMENTS_INTERFACE宏是怎么定义的,而又不小心改动了IID_ICat的定义而忙活了一整天。 Linux的怪圈 在刚开始阅读Linux的时候有一个小小的宏让我百思不得其解:#define wait_event(wq,condition) \do{ \if(condition) \break; \__wait_event(wq,condition); \}while(0) 这是一个奇怪的循环,它根本就只会运行一次,为什么不去掉外面的do{..}while结构呢?我曾一度在心里把它叫做“怪圈”。原来这也是非常巧妙的技巧。在工程中可能经常会引起麻烦,而上面的定义能够保证这些麻烦不会出现。下面是解释: 假设有这样一个宏定义#define macro(condition) \if(condition) dosomething(); 现在在程序中这样使用这个宏:if(temp)macro(i);elsedoanotherthing(); 一切看起来很正常,但是仔细想想。这个宏会展开成:if(temp)if(condition) dosomething();elsedoanotherthing(); 这时的else不是与第一个if语句匹配,而是错误的与第二个if语句进行了匹配,编译通过了,但是运行的结果一定是错误的。 为了避免这个错误,我们使用do{….}while(0) 把它包裹起来,成为一个独立的语法单元,从而不会与上下文发生混淆。同时因为绝大多数的编译器都能够识别do{…}while(0)这种无用的循环并进行优化,所以使用这种方法也不会导致程序的性能降低。 几个小小的警告 正如微软声称的一样,宏定义与预编译器指令是强大的,但是它又使得程序难以调试。所以在定义宏的时候不要节省你的字符串,一定要力争完整的描述这个宏的功能。同时在定义宏的时候如有必要(比方使用了if语句)就要使用do{…}while(0)将它封闭起来。在宏定义的时候一定要注意各个宏之间的相互依赖关系,尽量避免这种依赖关系的存在。下面就有这样一个例子。 设有一个静态数组组成的整型队列,在定义中使用了这样的方法: int array[]={5, 6, 7, 8}; 我们还需要在程序中遍历这个数组。通常的做法是使用一个宏定义#define ELE_NUM 4…………………………..……………………………..for(int I=0;I
在上一篇文章中,我演示了几个常用的宏定义和预处理指令,但可以说这些都是相当常规的技巧。下面要介绍的宏定义与预处理指令的用法也是ATL,MFC以及LINUX中使用得比较多的非常重要的技巧。 ## 连接符与# 符 ## 连接符号由两个井号组成,其功能是在带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串。但它不可以是第一个或者最后一个子串。所谓的子串(token)就是指编译器能够识别的最小语法单元。具体的定义在编译原理里有详尽的解释,但不知道也无所谓。同时值得注意的是#符是把传递过来的参数当成字符串进行替代。下面来看看它们是怎样工作的。这是MSDN上的一个例子。 假设程序中已经定义了这样一个带参数的宏:#define paster( n ) printf( "token" #n " = %d", token##n ) 同时又定义了一个整形变量:int token9 = 9; 现在在主程序中以下面的方式调用这个宏:paster( 9 ); 那么在编译时,上面的这句话被扩展为:printf( "token" "9" " = %d", token9 ); 注意到在这个例子中,paster(9);中的这个”9”被原封不动的当成了一个字符串,与”token”连接在了一起,从而成为了token9。而#n也被”9”所替代。 可想而知,上面程序运行的结果就是在屏幕上打印出token9=9 在ATL的编程中,我们查看它的源代码就会经常看见这样的一段:#define IMPLEMENTS_INTERFACE(Itf) \{&IID_##Itf, ENTRY_IS_OFFSET,BASE_OFFSET(_ITCls, Itf) }, 我们经常不假思索的这样使用它:……IMPLEMENTS_INTERFACE(ICat)…… 实际上IID_ICat 已经在别的地方由ATL向导定义了。当没有向导的时候,你只要遵循把IID_加在你的接口名前面来定义GUID的规则就也可以使用这个宏。在实际的开发过程中可能很少用到这种技巧,但是ATL使用得如此广泛,而其中又出现了不少这样的源代码,所以明白它是怎么一回事也是相当重要的。我的一个朋友就是因为不知道IMPLEMENTS_INTERFACE宏是怎么定义的,而又不小心改动了IID_ICat的定义而忙活了一整天。 Linux的怪圈 在刚开始阅读Linux的时候有一个小小的宏让我百思不得其解:#define wait_event(wq,condition) \do{ \if(condition) \break; \__wait_event(wq,condition); \}while(0) 这是一个奇怪的循环,它根本就只会运行一次,为什么不去掉外面的do{..}while结构呢?我曾一度在心里把它叫做“怪圈”。原来这也是非常巧妙的技巧。在工程中可能经常会引起麻烦,而上面的定义能够保证这些麻烦不会出现。下面是解释: 假设有这样一个宏定义#define macro(condition) \if(condition) dosomething(); 现在在程序中这样使用这个宏:if(temp)macro(i);elsedoanotherthing(); 一切看起来很正常,但是仔细想想。这个宏会展开成:if(temp)if(condition) dosomething();elsedoanotherthing(); 这时的else不是与第一个if语句匹配,而是错误的与第二个if语句进行了匹配,编译通过了,但是运行的结果一定是错误的。 为了避免这个错误,我们使用do{….}while(0) 把它包裹起来,成为一个独立的语法单元,从而不会与上下文发生混淆。同时因为绝大多数的编译器都能够识别do{…}while(0)这种无用的循环并进行优化,所以使用这种方法也不会导致程序的性能降低。 几个小小的警告 正如微软声称的一样,宏定义与预编译器指令是强大的,但是它又使得程序难以调试。所以在定义宏的时候不要节省你的字符串,一定要力争完整的描述这个宏的功能。同时在定义宏的时候如有必要(比方使用了if语句)就要使用do{…}while(0)将它封闭起来。在宏定义的时候一定要注意各个宏之间的相互依赖关系,尽量避免这种依赖关系的存在。下面就有这样一个例子。 设有一个静态数组组成的整型队列,在定义中使用了这样的方法: int array[]={5, 6, 7, 8}; 我们还需要在程序中遍历这个数组。通常的做法是使用一个宏定义#define ELE_NUM 4…………………………..……………………………..for(int I=0;I
下载本文示例代码
VC中预处理指令与宏定义的妙用之二VC中预处理指令与宏定义的妙用之二VC中预处理指令与宏定义的妙用之二VC中预处理指令与宏定义的妙用之二VC中预处理指令与宏定义的妙用之二VC中预处理指令与宏定义的妙用之二VC中预处理指令与宏定义的妙用之二VC中预处理指令与宏定义的妙用之二VC中预处理指令与宏定义的妙用之二VC中预处理指令与宏定义的妙用之二VC中预处理指令与宏定义的妙用之二VC中预处理指令与宏定义的妙用之二VC中预处理指令与宏定义的妙用之二VC中预处理指令与宏定义的妙用之二VC中预处理指令与宏定义的妙用之二
阅读(125) | 评论(0) | 转发(0) |