在C++的历史发展中,有很多的语言特征(特别是语言的晦涩之处)来自于C语言,预处理就是其中的一个.
预处理器可以通过预处理命令对一个资源进行等价替换, 预处理命令是一些以#
开头的单行命令. 常见的预处理包括: 宏替换,文件包含和条件编译. 下面将分别进行介绍.
宏替换
预处理器能够在源码文件中根据定义的规则进行文本替换, 这是最常见的用法,它可以定义符号常量、函数功能、重新命名、字符串的拼接等各种功能.
宏替换可以分为两种, 带参数的宏和不带参数的宏.
不带参数的宏语法规则如下:
#define 标识符 替换文本
//示例
#define PI 3.14
- 宏替换不是C++语句, 因此行末不需要添加分号, 如果添加了分号会被视为替换文本的一部分,
#define
命令同程出现在程序的开头部分,有效范围是从其定义处到当前文件结束. 但是可以使用#undef
命令终止宏定义的作用域,- 在定义宏时可以使用已经定义过的宏,
- 宏替换只作字符替换, 不会分配内存空间
带参数的宏语法规则如下:
#define 标识符(参数表) 替换文本
//示例
#define MAX(x,y) ((x) > (y) ? (x):(y))
带参数的宏看上去类似普通的C++函数, 两者之前的区别如下:
- 函数调用是在程序运行时处理的, 会为形参分配临时的内存. 而宏展开则是在编译前进行的, 展开时不分配内存,也不进行值传递,
- 宏名无类型, 参数也无类型. 而函数的参数和返回值都是有类型的,
- 宏替换不占用运行时间, 只占用编译时间. 而函数调用则占用运行时间
文件包含
文件包含是允许使用共享代码的一种特性, 就是指一个源文件可以将另一个源文件的全部内容包含进来.
文件包含的语法规则如下:
#include "文件名"
或
#include <文件名>
对于双引号包含, 编译器从标准库路径开始搜索; 对于尖括号包含, 编译器从用户的工作路径开始搜索.
条件编译
通常, 源文件中的所有内容都要被编译, 但在某些情况下, 可能希望部分代码只在满足某种条件时才进行编译, 这就是条件编译.
条件编译的语法形式主要有3种, 第一种如下:
#ifdef 标识符
//代码1
#else
//代码2
#endif
上述语句的意思是如果标识符被#define命令定义过, 则编译代码1, 否则编译代码2, 如果代码2为空, 则#else可以省略.
条件编译的第二种形式如下:
#ifndef 标识符
//代码1
#else
//代码2
#endif
上述语句的意思与形式一正好相反, 如果标识符没有被#define命令定义过, 则编译代码1, 否则编译代码2, 如果代码2为空, 则#else可以省略.
条件编译的第三种形式如下:
#if 标识符
//代码1
#else
//代码2
#endif
这种形式的写法意思是, 如果标识符的值为真(true), 则编译代码1, 否则编译代码2.
条件编译还常用于防止因为文件被重复包含而导致的类和函数的重定义问题:
#if !define(_POINT_H_)
#define _POINT_H_
class Point{
}
#endif
上面的例子中, 条件编译命令#if
用于检查是否定义了标识符_POINT_H_
.