C/C++ 编译预处理及条件编译

在编译器对源程序进行编译之前,首先要由预处理器对程序文本进行预处理。预处理提供了一组编译预处理指令和预处理操作符。预处理指令实际上不是 C++ 语言的一部分,它只是用来扩充 C++ 程序设计的环境。所有的预处理指令在程序中都是以 “#”来引导,每一条预处理指令单独占用一行,不要用分号结束。预处理指令可以根据需要出现在程序的任何位置。

1、#include 指令

#include 指令也称为文件包含指令,其作用是将另一个源文件嵌入到当前源文件该点处。通常用 #include 指令来嵌入头文件。文件包含指令有如下两种格式。

  1. #include <文件名>
    按标准方式搜索,文件位于系统目录下的 include 子目录下。
  2. #include “文件名”
    首先在当前目录中搜索,若没有,再按标准方式搜索。
    #include 指令可以嵌套使用。假设有一个头文件 myhead.h ,该头文件中又可以有如下的文件包含指令:
    #include "file1.h"
    #include "file2.h"
2、#define 和 #undef 指令

预处理器最初是为C语言设计的,#define 曾经在 C 程序中被广泛使用。但 #define 能完成的一些功能,能够被C++引入的一些语言特性很好的替代。
在C语言中,用 #define 来定义符号常量,例如下面的预编译指令定义了一个符号常量 PI 的值为 3.14:
#define PI 3.14
在C++中虽然仍然可以这样定义符号常量,但是更好的方法是在类型说明语句中用 const 进行修饰。
在C语言中,还可以用 #define 来定义带参数宏,以实现简单的函数计算,提高程序的运行效率,但是在C++中这一功能已经被内联函数取代。
用 #define 还可以定义空符号,例如:
#define MYHEAD_H
定义它的目的,仅仅是表示“MYHEAD_H 已经定义过”这样一种状态。将该符号配合条件编译指令一起使用,可以起到一些特殊作用,这是C++程序中 #define 的最常用之处。
#undef 的作用是删除由 #define 定义的宏,使之不再起作用。

3、条件编译指令

使用条件编译指令,可以限定程序中的某些内容要在满足一定条件的情况下参与编译。因此,利用条件编译可以使用一个源程序在不同的编译条件下产生不同的目标代码。例如,可以在调试程序时增加一些调试语句,以达到跟踪的目的,并利用条件编译指令,限定当程序调试好后,重新编译时,使调试语句不参与编译。常用的编译语句有下列 5 种形式。

  • 形式一
#if 常量表达式
        程序段         // 当“常量表达式”非零时编译本程序段
#endif
  • 形式二
#if 常量表达式
        程序段 1        // 当“常量表达式”非零时编译本程序段
#else 
        程序段 2        // 当“常量表达式”为零时编译本程序段
#endif
  • 形式三
#if 常量表达式 1
        程序段 1       // 当“常量表达式 1”非零时编译本程序段
#elif 常量表达式 2
        程序段 2       // 当“常量表达式 1”为零、“常量表达式 2” 非零时编译本程序段时编译本程序段
        .
        .
        .
#elif 常量表达式 n
        程序段 n       // 当“常量表达式 1”、···“常量表达式 n-1” 均为零、"常量表达式 n"非零时编译本程序段
#else
        程序段 n + 1 // 其它情况下编译本程序段
#endif
  • 形式四
#ifdef 标识符
        程序段 1
#else
        程序段 2
#endif

如果“标识符”经 #define 定义过,且未经 undef 删除,则编译程序段 1,否则编译程序段 2.如果没有程序段 2,则 #else 可以省略。

  • 形式五
#ifndef 标识符
        程序段 1
#else
        程序段 2
#endif

如果“标识符”未被定义过,则编译程序段 1,否则编译程序段 2。如果没有程序段 2,则 #else 可以省略。

4、defined 操作符

defined 是一个预处理操作符,而不是指令,因此不能以 # 开头。defined 操作符使用的形式为:
defined (标识符)
若“标识符”在此前经 #define 定义过,并且未经 #undef 删除,则上述表达式为非零,否则上述表达式的值为零。下面两种写法完全等效的。

#ifndef MYHEAD_H
#define NYHEAD_H
        ···
#endif     

等价于

#if ! defined(MYHEAD_H)
#define MYHEAD_H
        ···
#endif        

由于文件包含指令可以嵌套使用,在设计程序时要避免多次重复包含同一个头文件,否则会引起变量及类的重复定义。例如,某个工程包括如下 4 个源文件。

// main.cpp
#include "file1.h"
#include "file2.h"

int main()
{
    ···
}

// file1.h
#include "head.h"
    ···
    
// file2.h
#include "head.h"
    ···
    
// head.h
    ···
class Point
{
    ···
}
    ···

这时,由于 #include 指令的嵌套使用,使得头文件 head.h 被包含了两次,于是编译时系统会指出错误:类 Point 被重复定义。如何避免这种情况呢?这就要在可能被重复包含的头文件使用条件编译指令。用一个唯一的标识符来标记某文件是否已经参加过编译,如果已经参加过编译则说明该程序段是被重复包含的,编译时忽略重复部分。将文件 head.h 改写如下:

// head.h
#ifndef HEAD_H
#define HEAD_H
    ···
class Point
{
    ···
}
    ···
#endif

在这个头文件中,首先判断标识符 HEAD_H 是否被定义过。若未定义过,说明此头文件尚未参加过编译,于是编译下面的程序段,并且对标识符 HEAD_H 进行宏定义,标记此文件已参加过编译。若标识符 HEAD_H 被定义过,说明此头文件参加过编译,于是忽略下面的程序段。这样便不会造成对类 Point 的重复定义。

你可能感兴趣的:(C/C++,C/C++,#define,编译预处理,条件编译)