14、预处理宏

预处理宏

预处理器(Preprocessor)定义了读取源代码、对代码预先翻译以及编写供编译器读取的新代码的过程。预处理先于编译器对源代码进行处理。

C/C++ 语言没有内置工具在编译时间包含其他源文件、宏定义,或根据条件包含或排除一些代码行的编译时指令。预处理器提供了这些能力。虽然当前大多数编译器内部集成了预处理器,人们还是认为预处理独立于编译器的过程。预处理器读取源代码,查找预处理指令语句和宏调用,然后翻译源代码,它还去掉程序中的注释和多余的空白。

  在C++语言中预处理指令有:

 

指令

功能描述

#

空指令,没有作用

#include

在指令的位置包含一个源代码文件

#define

定义一个宏

#undef

取消宏定义

#if

如果给定条件为真,则编译代码

#ifdef

如果宏被定义,则编译代码

#ifndef

如果宏未被定义,则编译代码

#elif

如果前面的#if...条件不为真而当前条件为真,则编译代码

#endif

终止#if....#else条件块

#error

终止编译并显示错误信息

#line

修改编译器尾部用于消息报告的文件名和行号

#pragma

功能取决于平台

"#"串化运算符

  宏定义内的"#"运算符把位于其后的形参所对应的实参转化为字符串。

  例子:

#include <iostream.h>

  #define Error(n) cout << "Error " << #n

  int main(void)

{

     Error(53);

     return 0;  

}

 

程序运行结果:Error 53

http://www.cnblogs.com/mydomain/archive/2010/09/25/1834917.html

  "##"运算符

  "##"运算符把多个实参连接起来。

  #include <iostream.h>

   #deifne BookChapterVerse(b,c,v) b ## c ## v

  int main(void)

{

     unsigned bcv=BookChapterVerse(5,12,43);

     cout << bcv;

     return 0;

}

#include   在指令的位置包含一个源代码文件

头文件通常以.h结尾,其 内容可使用#include预处理器指令包含到程序中。 头文件中一般包含: 函数原型与全局变量

  形式常有下面两种

  #include <iostream>

  #include "myheader.h"

  前者<>用来引用标准库头文件,后者""常用来引用自定义的头文件

    前者<>编译器只搜索包含标准库头文件的默认目录,后者首先搜索正在编译的源文件所在的目录,找不到时再搜索包含标准库头文件的默认目录.

  如果把头文件放在其他目录下,为了查找到它,必须在双引号中指定从源文件到头文件的完整路径

  #define  定义符号、宏符号

  #define PI 3.1415925  定义符号PI3.1415925

  #define PI      取消PI的值

或有#undef PI

    3.14159265 不是一个数值,只是一个字符串,不会进行检查

    在编译前,预处理器会遍历代码,在它认为置换有意义的地方,用字符串PI的定义值(3.14159265)来代替

   在注释或字符串中的PI不进行替换

  在C中常以#define来定义符号常量,但在C++中最好使用const 来定义常量

  #define PI 3.14159265

  const long double PI=3.14159265;

    两者比较下,前者没有类型的指定容易引起不必须的麻烦,而后者定义清楚,所以在C++中推荐使用const来定义常量

   #define的缺点:

   1)不支持类型检查

   2)不考虑作用域

   3)符号名不能限制在一个命名空间中

  用宏名中的参数带入语句中的参数

 

  #define Print(Var, digits)  count << setw(digits) << (Var) << endl

宏后面没有;

Print(Var)中的Print(之间不能有空格,否则(就会被解释为置换字符串的一部分调用

  Print(ival, 15);

  预处理器就会把它换成

  cout << setw(15) << (ival) << endl;

  所有的情况下都可以使用内联函数来代替宏,这样可以增强类型的检查

  template<class T>

  inline void Print (const T& var, const int& digits)

  {

      count<<setw(digits)<<var<<endl;

  }

  调用

  Print(ival, 15);

逻辑预处理器指令#if  #else  #elif   #endif   #undef   #ifndef

 #if defined CALCAVERAGE 或 #ifdef CALCAVERAGE

   int count=sizeof(data)/sizeof(data[0]);

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

     average += data;

   average /= count;

  #endif

  如果已经定义符号CALCAVERAGE则把#if#endif间的语句放在要编译的源代码内

  防止重复引入某些头文件

  #ifndef COMPARE_H

  #define COMPARE_H    

注意:这里只是定义一个没有值的符号COMPARE_H, 下面的namespace compare不是COMPARE_H的 内容,这里的定义不像是定义一个常量或宏,仅仅定义一个符号,指出此符号已定义,则就会有下面的内容

   namespace compare{

     double max(const double* data, int size);

     double min(const double* data, int size);

   }

  #endif

  比较

  #define VERSION \

   3

  因为有换行符\ 所以上句等价于 #define VERSION 3

  由此可以看出#define COMPARE_Hnamespace compare是独立没有关系的两个行。

#line  

  使用#line可以修改__FILE__返回的字符串

  如

  #line 1000    把当前行号设置为1000

  #line 1000 "the program file"      修改__FILE__返回的字符串行号改为了1000,文件名改为了"the program file"

  #line __LINE__ "the program file"  修改__FILE__返回的字符串行号没变,文件名改为了"the program file"

#error

  在预处理阶段,如果出现了错误,则#error指令可以生成一个诊断消息,并显示为一个编译错误,同时中止编译

  #ifndef __cplusplus

  #error "Error -  Should be C++"

  #endif

#pragma

专门用于实现预先定义好的选项,其结果在编译器说明文档中进行了详细的解释。编译器未识别出来的#pragma指令都会被忽略。

assert()

在标准库头文件<cassert>中声明

用于在程序中 测试一个逻辑表达式,如果逻辑表达式为false, assert()会终止程序,并显示诊断消息

用于在条件不满足就会出现重大错误,所以应确保后面的语句不应再继续执行,所以它的应用非常灵活。

注意:assert不是错误处理 机制,逻辑表达式的结果不应产生负面效果,也不应超出程序员的控制(如找开一个文件是否成功), 程序应提供适当的代码来处理这种情况

 assert(expression);

  assert(expression) && assert(expression2);

  可以使用#define NDEBUG来关闭断言机制

参考

1http://www.cnblogs.com/sdywcd_coffee/archive/2010/01/06/1640653.html

posted @ 2010-09-27 21:53  浪里飞  阅读(648)  评论(0编辑  收藏  举报