注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

The Bloom of Youth

本博客已搬家至http://kuangqi.me

 
 
 

日志

 
 

关于C/C++“宏”的技术规范和使用详解  

2011-01-27 23:25:17|  分类: 编程之美 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

宏替换是C/C++系列语言的技术特色,C/C++语言提供了强大的宏替换功能,源代码在进入编译器之前,要先经过一个称为“预处理器”的模块,这个模块将宏根据编译参数和实际编码进行展开,展开后的代码才正式进入编译器,进行词法分析、语法分析等等。

我们常用的宏替换主要有这么几个类型。

1.宏常量

ACM等算法竞赛中,经常会把数组的最大下标通过宏定义的方法给出,以方便调试,例如:

#define MAX 1000

int array[MAX][MAX]

......

for(int i = 0; i < MAX; i++)

......

将一个数字定义成全局的常量,这个用法在国产垃圾教材上十分常见。但在经典著作《Effective C++》中,这种做法却并不提倡,书中更加推荐以const常量来代替宏常量。因为在进行词法分析时,宏的引用已经被其实际内容替换,因此宏名不会出现在符号表中。所以一旦出错,看到的将是一个无意义的数字,比如上文中的1000,而不是一个有意义的名称,如上文中的MAX。而const在符号表中会有自己的位置,因此出错时可以看到更加有意义的错误提示。

 

2.用于条件编译标识的宏

#ifndef _HEADER_INC_

#define _HEADER_INC_

……

……

#endif

这种宏标记在头文件中十分常见,用于防止头文件被反复包含。应该养成习惯在每个头文件中都添加这种标记。

还有一种用于条件编译的用法

#ifdef DEBUG

printf(“Debug information\n”);

#endif

通过DEBUG宏,我们可以在代码调试的过程中输出辅助调试的信息。当DEBUG宏被删除时,这些输出的语句就不会被编译。更重要的是,这个宏可以通过编译参数来定义。因此通过改变编译参数,就可以方便的添加和取消这个宏的定义,从而改变代码条件编译的结果。

 

3.宏函数

宏函数是技巧性最强,也最灵活的应用。在实际开发中也有着广泛的应用。在编程过程中恰当的使用宏定义和宏函数,可以有效的缩减代码长度,提高执行效率。在MFC的头文件中就随处可见一些复杂貌似的宏函数。而大名鼎鼎的assert宏也是一个宏函数。这些宏函数都经过了精心设计,其重要作用不言而喻。

例如在前段时间的数据结构课程作业中,要求按年、月、日、季度、星期查询数据。其代码相似程度很高,用复制粘贴的方法容易出错,也影响可读性和可维护性。可以使用宏函数来实现代码的“复制粘贴”。宏函数的语法有以下特点:

1.如果需要换行,则行末要加反斜杠“\”表示换行。宏函数体的最后一行不加反斜杠。

2.假设有参数ARGU,值为argu,则所有的ARGU被直接替换为argu#ARGU被认为是字符串,会被替换成”argu”(带引号)。

3.由于宏函数是直接替换,所有一般情况下对宏函数的调用时行末不需要加分号。

 

下面从我的数据结构课程作业中摘录一段:

    //宏函数

    #define QUERY(OPERATOR) else if(opr == #OPERATOR )\

    {   cin >> num;\

        Header();\

        for(int i = 0; i < count; i++)\

        {   if(code)\

            {if(vec[i].code == code && vec[i].OPERATOR == num) cout << vec[i];}\

            else\

            {if(vec[i].name == name && vec[i].OPERATOR == num) cout << vec[i];}\

        }\

    }

    //-------------------宏函数结束---------------------

 

    //以下是宏函数调用,不加分号

    QUERY(week)

    QUERY(month)

    QUERY(quarter)

QUERY(year)

 

oprstring型变量,因此opr == #OPERATOR一句加了井号。

QUERY(week)为例,opr == #OPERATOR替换后的结果是opr == “week”,而vec[i].OPERATOR替换的结果是vec[i].week

初学者也许会在使用宏函数的时候遇到一些困难,如果你的编译不能通过,你也许会想要查看一下宏替换后的代码是啥样。也好知道哪里错了。幸运的是GCC编译器提供给我们了这样的功能:

gcc -E xxx.c -o xxx.i

xxx.c是源码,xxx.i是替换后的结果。GCC会读取xxx.c进行替换,将结果输出到xxx.i

友情提示,建议在处理前去掉所有的头文件,否则结果会很纠结……宏替换不会检查代码的语法,所以不要担心去掉头文件后会出现变量或者类型未定义之类的错误。

  评论这张
 
阅读(1018)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018