电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

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


发布日期:2020/2/23
 

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

一个经典的例子

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

#include headerfileh

#include headerfileh

假设headerfileh 中定义了一个全局变量 iglobal

int iglobal

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

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

#ifndef smartnose____headerfile_h

#define smartnose____headerfile_h

int iglobal

#endif

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

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

#pragma once

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

源代码版本控制

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

main()

{

……………………

#ifdef _WINDOWS_PLATFORM

HeapAlloc(

#else

malloc(

#endif

……………………

}

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

#ifdef _DEBUG

……………………some code……………

#endif

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

#Pragma 指令

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

#Pragma Para

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

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

#Pragma message(消息文本

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

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

#ifdef _X

#Pragma message(_X macro activated!

#endif

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

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

#pragma code_seg( [sectionname[sectionclass] ] )

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

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

VC预定义的宏

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

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

try

{

char * p=new(char[])

}

catch(CException *e )

{

TRACE( there is an error in file %s\n__FILE__)

}

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

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

#define MyAssert(x) \

if(!(x)) \

MessageBox(__FILE____LINE__NULLMB_OK)

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

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

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

结论

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

上一篇:怎样将成员函数指针强制转换成void*指针?

下一篇:Cookies揭秘