现代C++中的预处理宏

现代C++中的预处理宏

--徐东来

摘要:在C++从C继承的遗产中,预处理宏是其中的一部分。在现代C++的发展过程中,预处理宏是否还有意义?本文将讨论之。

关键字:预处理 宏 #define #pragma

 

   C++中有那么多灵活的特性,例如重载、类型安全的模板、const关键字等等,为什么程序员还要写“#define”这样的预处理指令?

   典型的一个例子,大家都知道“const int a=100;”就比“#define a 100”要好,因为const提供类型安全、避免了预处理的意外修改等。

   然而,还是有一些理由让我们去使用#define。

一、使用预处理宏

1)   守护头文件

为了防止头文件被多次包含,这是一种常用技巧。

#ifndef MYPROG_X_H

#define MYPROG_X_H

// … 头文件x.h的其余部分

#endif

2)   使用预处理特性

在调试代码中,插入行号或编译时间这类信息通常很有用,可以使用预定义的标准宏,例如__FILE__、__LINE__、__DATE__和__TIME__。

3)   编译时期选择代码

A.  调试代码

选择性的输出一些调试信息:

void f()

{

#ifdef _DEBUG

   cerr<<”调试信息”<<endl;

#endif

// .. f()的其他部分

}

通常我们也可以用条件判断来代替:

void f()

{

   if(_DEBUG)

   {

   cerr<<”调试信息”<<endl;

}

// .. f()的其他部分

}

B.  特定平台代码

同一函数同一功能在不同的编译平台上可能有不同的表现形式,我们可以通过定义宏来区分不同的平台。

C.  不同的数据表示方式

<<深入浅出MFC>>这本书对MFC框架中宏的使用解析的很透彻,也让我们领略到宏的强大功能。可以参看DECLARE_MESSAGE_MAP(),

BEGIN_MESSAGE_MAP, END_MESSAGE_MAP的实现。

4)   #pragma的使用,例如用#pragma禁止掉无伤大雅的警告,用于可移植性的条件编译中。例如,

包含winsock2 lib文件:

#pragma comment(lib,”ws2_32”)

用如下预处理宏,可以使结构按1字结对齐:

#pragma pack(push)

#pragma pack(1)

// … 结构定义

#pragma pack(pop)

      禁止掉某些警告信息:

#pragma warning( push )

#pragma warning( disable : 4705 )

#pragma warning( disable : 4706 )

#pragma warning( error : 164 )// 把164号警告作为错误报出

// Some code

#pragma warning( pop )

 

二、宏的常见陷阱

   下面示范如何写一个简单的预处理宏max();这个宏有两个参数,比较并返回其中较大的一个值。在写这样一个宏时,容易犯哪些错误?有四大易犯错误。

1)   不要忘记为参数加上括号

// 例1:括号陷阱一:参数

//

#define max(a, b) a < b ? b : a

例如:

max(i += 2, j)

展开后:

i += 2 < j ? j : i += 2

考虑运算符优先级和语言规则,实际上是:

i += ((2 < j) ? j : i += 2)

这种错误可能需要长时间的调试才可以发现。

2)   不要忘记为整个展开式加上括号

// 例2:括号陷阱二:展开式

//

#define max(a, b) (a) < (b) ? (b) : (a)

   例如:

   m = max(j, k) + 42;

   展开后为:

   m = (j) < (k) ? (j) : (k) + 42;

考虑运算符优先级和语言规则,实际上是:

   m = ((j) < (k)) ? (j) : ((k) + 42);

   如果j >= k, m被赋值k+42,正确;如果j < k, m被赋值j,是错误的。如果给展开式加上括号,就解决了这个问题。

3)   当心多参数运算

// 例3:多参数运算

//

#define max(a, b) ((a) < (b) ? (b) : (a))

max(++j, k);

   如果++j的结果大于k,j会递增两次,这可能不是程序员想要的:

((++j) < (k) ? (k) : (++j))

   类似的:

max(f(), pi)

展开后:

((f()) < (pi) ? (pi) : (f()))

如果f()的结果大于等于pi,f()会执行两次,这绝对缺乏效率,而且可能是错误的。

4)   名字冲突

宏只是执行文本替换,而不管文本在哪儿,这意味着只要使用宏,就要小心对这些宏命名。具体来说,这个max宏最大的问题是,极有可能会和标准的max()函数模板冲突:

// 例4:名字冲突

//

#define max(a,b) ((a) < (b) ? (b) : (a))

#include <algorithm> // 冲突!

在<algorithm>中,有如下:

template<typename T> const T&

max(const T& a, const T& b);

宏将它替换为如下,将无法编译:

template<typename T> const T&

((const T& a) < (const T& b) ? (const T& b) : (const T& a));

所以,我们尽量避免命名的冲突,想出一个不平常的,难以拼写的名字,这样才能最大可能地避免与其他名字空间冲突。

 

宏的其他缺陷:

5)   宏不能递归

   容易理解。

6)   宏没有地址

你可能得到任何自由函数或成员函数的指针,但不可能得到一个宏的指针,因为宏没有地址。宏之所以没有地址,原因很显然===宏不是代码,宏不会以自身的形势存在,因为它是一种被美化了的文本替换规则。

7)   宏有碍调试

在编译器看到代码之前,宏就会修改相应的代码,因而,他会严重改变变量名称和其他名称;此外,在调试阶段,无法跟踪到宏的内部。

时间: 2024-09-24 07:35:28

现代C++中的预处理宏的相关文章

C#中的预处理指令

与C++不同,C#没有独立的预处理器.C#中的预处理指令(pre-processing directives)仅仅用来与C保持一致,而并不是编译器开始编译代码之前的一个单独的处理步骤,它是作为词法解析的一部分来执行的. 预处理指令都以#号开头并位于行首(前面可以出现空格符).在介绍条件编译之前,我们先学习两条用于定义符号和取消符号定义的预处理指令:#define和#undef. #define指令对于有一点C语言知识的读者来说再熟悉不过,它非常类似于C中的宏定义: #define COUNT 这

详解C语言中的#define宏定义命令用法_C 语言

#define命令#define定义了一个标识符及一个串.在源程序中每次遇到该标识符时,均以定义的串代换它.ANSI标准将标识符定义为宏名,将替换过程称为宏替换.命令的一般形式为: #define identifier string 注意: 1.该语句没有分号.在标识符和串之间可以有任意个空格,串一旦开始,仅由一新行结束. 2.宏名定义后,即可成为其它宏名定义中的一部分. 3.宏替换仅仅是以文本串代替宏标识符,前提是宏标识符必须独立的识别出来,否则不进行替换.例如: #define XYZ th

Excel中如何录制“宏”命令动画教程

<Excel2003入门动画教程60.Excel中如何录制"宏"命令>. 演示动画 操作步骤 完全可以这样说,"宏"是Excel的精华部分,如何录制一个宏呢? 下面,我们以录制一个设置标题"跨列居中"的宏为例,看看具体的录制过程: 执行"工具→宏→录制新宏"命令,打开"录制新宏"对话框. 在"宏名"下面输入一个名称(如"跨列居中"),并设置好宏的保存位置.

Excel中加载宏动画教程

<Excel2003入门动画教程54.Excel中加载宏>. 演示动画 操作步骤 先将加载宏文档保存到相应的文件夹中,然后在Excel加载使用. 执行"工具加载宏"命令,打开"加载宏"对话框,点击"浏览"按钮,打开"浏览"对话框,选中需要加载的宏,依次确定返回. 注意:①如果是系统自身的加载,直接在"加载宏"窗口中选择相应的宏,确定即可.②如果你找不到加载宏文件夹,不能复制文件到其中,可以这样操

Word2013中怎样录制宏

Word 2013中录制宏步骤1: 启动Word 2013并打开文档,在文档中输入需要进行处理的文字,然后在"视图"选项卡的"宏"组中单击"宏"按钮,在打开的列表中选择"录制宏"选项,如图1所示.此时将打开"录制宏"对话框,在"宏名"文本框中输入宏的名称,在"将宏保存在"下拉列表中选择宏保存的文档,这里选择当前打开的文档,如图2所示. 选择"录制宏"

Excel中怎么运行宏?

  可以采用多种方法运行宏.您始终可以使用菜单命令运行宏.您也可以通过按 Ctrl 组合快捷键.单击工具栏按钮或单击对象.图形或控件上的某个区域来运行宏,具体取决于为宏指定的运行方式.另外,您可以在打开工作簿时自动运行宏.下面有个在运行宏过程中小问题,不知大家有没有遇到. 描述:有写好的宏要运行,老是提示不行,降低了安全级别 了还是提示要改一些软件的参数,是不是运行宏真的很麻烦,要怎么弄啊 解决步骤: 1.工具-宏-录制新宏,会出现对话框,设置快捷按钮(编辑区会出现一个小工具栏,先不管它) 2.

如何利用access 2010 中的数据宏功能进行逻辑判断操作,以达到新增 的记录符合既定的要求

问题描述 如何利用access 2010 中的数据宏功能进行逻辑判断操作,以达到新增 的记录符合既定的要求 例如,两张表,主表为A,明细表为B,当A表中的某一记录中的一个字段值为TRUE的时候,与A表此记录一对多相关联的B表所有记录不能修改,不能删除,及不能在B表中新增任何与A表此记录相关联的新记录. A表数据结构 字段名称: ID 审核 A 数据类型: 自动编号 是/否 文字串 B表数据结构 字段名称: ID 审核 A A_ID 数据类型: 自动编号 是/否 文字串 长整数 主链字段:A.ID

求解word2010 中如何用宏去掉状态栏中的字数统计

问题描述 求解word2010 中如何用宏去掉状态栏中的字数统计 如何用宏去掉word2010中 自定义状态栏上的字数统计, 或者隐藏word2010中的自定义状态栏也行. 别说是视图-> 勾选状态栏 word2010中没有这个功能.

线程-mfc中定义大量宏的问题

问题描述 mfc中定义大量宏的问题 我定义了大量的宏,在cmainframe和一个共有类(用于存放一些静态函数和变量,主要是为了线程函数中使用全局变量)中里面的一个函数中需要使用到,我直接把它们放在一个头文件中然后include,就报了error LNK2005: ""struct Zeni_MTS_Rolypon_InOut InOutRoly"" (?InOutRoly@@3UZeni_MTS_Rolypon_InOut@@A) already defined