VC中预处理指令与宏定义的妙用

转载 2004年07月21日 14:02:00

(1)   在VC中有一类宏并不是由用户用#define语句定义的,而是编译器本身就能够识别它们。这些宏的作用也是相当大的。让我们来看第一个,也是MFC中使用得最频繁的一个:__FILE__ 。

  当编译器遇到这个宏时就把它展开成当前被编译文件的文件名。好了,我们马上就可以想到可以用它来做什么,当应用程序发生错误时,我们可以报告这个错误发生的程序代码在哪个文件里,比方在文件test.cpp中有这样的代码:

try

{

char * p=new(char[10]);

}

catch(CException *e )

{

TRACE(“ there is an error in file: %s/n”,__FILE__);

}

在程序运行的时候,如果内存分配出现了错误,那么在调试窗口中会出现there is an error in file: test.cpp 这句话,当然,我们还可以把这个错误信息显示在别的地方。

如果我们还能够记录错误发生在哪一行就好了,幸运的是,与__FILE__宏定义一样,还有一个宏记录了当前代码所在的行数,这个宏是__LINE__。使用上面的两个宏,我们可以写出一个类似于VC提供的ASSERT语句。下面是方法

#define MyAssert(x) /

  if(!(x)) /

MessageBox(__FILE__,__LINE__,NULL,MB_OK);

我们在应用程序中可以象使用ASSERT语句一样使用它,在错误发生时,它会弹出一个对话框,其标题和内容告诉了我们错误发生的文件和代码行号,方便我们的调试,这对于不能使用ASSERT语句的项目来说是非常有用的。

除了这两个宏以外,还有记录编译时间的__TIME__,记录日期的__DATE__,以及记录文件修改时间的__TIMESTAMP__宏。

使用这些预定义的宏,我们几乎可以生成和VC能够生成的一样完整的源代码信息报表。

 

(2)  当我们为许多平台开发多个版本的时候预编译指令和宏定义也能够帮我们的忙。假设我们现在为WINDOWS 和LINUX开发了一套软件,由于这两种系统的不同,我们不得不在程序控制源代码的版本。比方内存的分配,我们可以在LINUX上使用标准C的malloc 函数,但是我们希望在WINDOWS上使用HeapAlloc API。下面的代码演示了这种情况:

 

main()

{

………………..

#ifdef _WINDOWS_PLATFORM

HeapAlloc(5);

#else

malloc(5);

#endif

………………..

}

当我们在WINDOWS 平台上编译此程序的时候,只需要定义_WINDOWS_PLATFORM这个宏,那么HeapAlloc这条语句就能够起作用了。这样就能够让我们在同一个文件中为不同的平台实现不同版本的代码,同时保持程序的良好结构。在许多情况下,我们还可以为一个方法使用不同的算法,然后用宏定义来针对不同的情况选择其中的一个进行编译。这在MFC应用程序中是使用得最多的。最明显的就是文件中经常存在的

#ifdef _DEBUG

…………………….some code………..

#endif

这样的代码,这些代码在应用程序的调试版(DEBUG)中会发挥其作用。

#Pragma 指令
在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。其格式一般为

#Pragma Para

其中Para 为参数,下面来看一些常用的参数。

message 参数。 Message 参数是我最喜欢的一个参数,它能够在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:

#Pragma message(“消息文本”)

当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。

当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法

#ifdef _X86

#Pragma message(“_X86 macro activated!”)

#endif

当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了。

另一个使用得比较多的pragma参数是code_seg。格式如:

#pragma code_seg( ["section-name"[,"section-class"] ] )

它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。

最后一个比较常用的就是上面所说的#pragma once 指令了。

(3)    刚接触到MFC编程的人往往会被MFC 向导生成的各种宏定义和预处理指令所吓倒,但是预处理和宏定义又是C语言的一个强大工具。使用它们可以进行简单的源代码控制,版本控制,预警或者完成一些特殊的功能。

 


一个经典的例子
 

使用预处理与宏定义最经典的例子莫过于加在一个头文件中以避免头文件被两次编译。试想这种的情况,有一个文件headerfile.h 它被包含在headerfile1.h中,同时在headerfile2.h 中也被包含了,现在有一个CPP文件,implement.cpp 包含了headerfile1.h 和headerfile2.h:

#include “headerfile1.h”

#include “headerfile2.h”

假设headerfile.h 中定义了一个全局变量 iglobal 。

int iglobal;

在编译的时候编译器两次编译headerfile,也就会发现iglobal被定义了两次,这时就会发生变量重定义的编译错误。

传统的解决办法是使用#ifdef 以及#endif 来避免头文件的重复编译,在上面的例子中,只需要加上这么几行:

#ifndef smartnose_2002_6_21_headerfile_h

#define smartnose_2002_6_21_headerfile_h

int iglobal;

#endif

仔细的考虑上面的宏定义,会发现当编译器编译过一次headerfile.h以后,smartnose_2002_6_21_headerfile_h 这个宏就被定义了,以后对headerfile.h的编译都会跳过int iglobal 这一行。当然smartnose_2002_6_21_headerfile_h 这个宏是可以任意定义的,但是这个宏本身不能和其它文件中定义的宏重复,所以MFC在自动生成的文件中总是使用一个随机产生的长度非常长的宏,但我觉得这没有必要,我建议在这个宏中加入一些有意义的信息,比方作者,文件名,文件创建时间等等,因为我们有时候会忘记在注释中加入这些信息。

在VC.Net 中我们不会再看见这些宏定义了,因为在这里会普遍使用一个预处理指令:

#pragma once

只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。

结论
翻开MFC和Linux的源代码,宏定义几乎占据了半边天,消息映射,队列操作,平台移植,版本管理,甚至内核模块的拆卸安装都用宏定义完成。毫不夸张的说,有些文件甚至就只能看见宏定义。所以学习宏定义,熟练的使用宏定义对于学习C语言乃至VC都是非常关键的。

C/C++编译预处理:宏定义指令、文件包含指令、条件编译指令和特殊符号处理

从源代码生成可执行文件的各个阶段为: C源程序(.c)->编译预处理(.i)->编译(.s)->优化程序->汇编程序(.o)->链接程序->可执行文件(.exe)   其中 编译预处理阶段,预处理...
  • u012209626
  • u012209626
  • 2015年08月17日 21:42
  • 1600

33.C语言宏定义和预处理

33.1.源码到可执行程序过程 33.2.预处理的意义及编程应用 33.3.头文件包含 33.4.注释和条件编译 33.5.宏定义的规则和使用解析 33.6.带参宏和带参函数的区别 33.7.内联函数...
  • Rston
  • Rston
  • 2016年04月09日 14:55
  • 1334

VC中的宏 (#define) 与预处理 (#if/#ifdef/#pragma) 的使用方法总结。

VC 中的宏使用方法参考 MSDN: Macros (C/C++) "src="/CuteSoft_Client/CuteEditor/Images/anchor.gif">"src="/CuteS...
  • whatday
  • whatday
  • 2014年07月21日 12:17
  • 10499

VC预处理指令与宏定义的妙用

VC中预处理指令与宏定义的妙用   刚接触到MFC编程的人往往会被MFC 向导生成的各种宏定义和预处理指令所吓倒,但是预处理和宏定义又是C语言的一个强大工具。使用它们可以进行简单的源...
  • ningyaliuhebei
  • ningyaliuhebei
  • 2012年05月08日 11:07
  • 2372

VC中预处理指令与宏定义的妙用之二

2006-09-07 07:00作者:出处:blog责任编辑:方舟   在上一篇文章中,我演示了几个常用的宏定义和预处理指令,但可以说这些都是相当常规的技巧。下面要介绍的宏定义与预处理指令的用法也是...
  • summer__xt
  • summer__xt
  • 2012年04月01日 16:39
  • 275

VC预处理指令与宏定义的妙用

VC预处理指令与宏定义的妙用 分类: C C++ 计算机语言 2011-09-10 16:34 21人阅读 评论(0) 收藏 举报   VC预处理指令与宏定义的妙用 ...
  • junecauzhang
  • junecauzhang
  • 2012年02月17日 23:15
  • 323

15-预处理指令1-宏定义、条件编译、文件包含

1.C语言在对源程序进行编译之前,会先对一些特殊的预处理指令作解释(比如之前使用的#include文件包含指令),产生一个新的源程序(这个过程称为编译预处理),之后再进行通常的编译 2.为了区分预处...
  • helainthus
  • helainthus
  • 2015年01月22日 14:44
  • 130

【C语言】13 预处理指令1-宏定义

1.预处理指令简介 1.C语言在对源程序进行编译之前,会先对一些特殊的预处理指令作解释(比如之前使用的#include文件包含指令),产生一个新的源程序(这个过程称为编译预处理),之后再进行通常的编译...
  • lijin0426
  • lijin0426
  • 2014年05月15日 14:29
  • 196

基础C ,预处理指令,宏定义,文件包含

16.static 与 extern 是什么c语言关键字 17. 有什么用? 答:控制一个变量或者函数的作用域. 18.怎么用? 答:修饰函数:static 返回值 函数名(){};表示函数为内部函...
  • jeffdy66
  • jeffdy66
  • 2015年12月07日 23:03
  • 157

C语言-预处理指令1-宏定义

本文目录 预处理指令简介一、不带参数的宏定义二、带参数的宏定义 说明:这个C语言专题,是学习iOS开发的前奏。也为了让有面向对象语言开发经验的程序员,能够快速上手C语言。如果你还没有编程经...
  • smxueer
  • smxueer
  • 2014年10月10日 11:54
  • 696
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:VC中预处理指令与宏定义的妙用
举报原因:
原因补充:

(最多只允许输入30个字)