C++ 预处理 简介
一、常见的预处理功能:预处理器的主要作用就是把通过预处理的内建功能对一个资源进行等价替换,最常见的预处理有:文件包含,条件编译、布局控制和宏替换四种。
文件包含:#include 是一种最为常见的预处理,主要是做为文件的引用组合源程序正文。
条件编译:#if,#ifndef,#ifdef,#endif,#undef等也是比较常见的预处理,主要是进行编译时进行有选择的挑选,注释掉一些指定的代码,以达到版本控制、防止对文件重复包含的功能。
布局控制:#progma,这也是我们应用预处理的一个重要方面,主要功能是为编译程序提供非常规的控制流信息。
宏替换: #define,这是最常见的用法,它可以定义符号常量、函数功能、重新命名、字符串的拼接等各种功能。
二、预处理指令:
#define // 宏定义
#undef // 未定义宏
#include // 文本包含
#ifdef // 如果宏被定义就进行编译
#ifndef // 如果宏未被定义就进行编译
#endif // 结束编译块的控制
#if // 表达式非零就对代码进行编译
#else // 作为其他预处理的剩余选项进行编译
#elif // 这是一种#else和#if的组合选项 // 后面有例子的
#line // 改变当前的行数和文件名称
#error // 输出一个错误信息
#pragma // 为编译程序提供非常规的控制流信息
三、文件包含指令:
#include <iostream> // 标准库头文件
#include "IO.h" // 用户自定义的头文件
#include "../file.h" // UNIX下的父目录下的头文件
#include "/usr/local/file.h" // UNIX下的完整路径
#include "..file.h" // Dos下的父目录下的头文件
#include "usrlocalfile.h" // Dos下的完整路径
四、编译控制指令:
这些指令的主要目的是进行编译时进行有选择的挑选,注释掉一些指定的代码,以达到版本控制、防止对文件重复包含的功能。使用格式,如下:
1、如果identifier为一个定义了的符号,your code就会被编译,否则剔除
#ifdef identifier
// your code
#endif
2、如果identifier为一个未定义的符号,your code不会被编译
#ifndef identifier
// your code
#endif
3、如果expression非零,your code就会被编译,否则剔除
#if expression
// your code
#endif
4、如果identifier为一个定义了的符号,your code1就会被编译,否则your code2就会被编译
#ifdef identifier
// your code1
#else
// your code2
#endif
5、如果epression1非零,就编译your code1,否则,如果expression2非零,就编译your code2,否则,就编译your code3
#if expressin1
// your code1
#elif expression2
// your code2
#else
// your code3
#enif
其他预编译指令##
还有3种不太常见的编译指令:#line、#error、#pragma
#line的语法如下:
#line number filename
例如:#line 30 a.h 其中,文件名a.h可以省略不写。
这条指令可以改变当前的行号和文件名,例如上面的这条预处理指令就可以改变当前的行号为30,文件名是a.h。初看起来似乎没有什么用,不过,他还是有点用的,那就是用在编译器的编写中,我们知道编译器对C++源码编译过程中会产生一些中间文件,通过这条指令,可以保证文件名是固定的,不会被这些中间文件代替,有利于进行分析。
#error语法如下:
#error info
例如:
#ifndef UNIX
#error This software requires the UNIX OS.
#endif
这条指令主要是给出错误信息,上面的这个例子就是,如果没有在UNIX环境下,就会输出This software requires the UNIX OS.然后诱发编译器终止。所以总的来说,这条指令的目的就是在程序崩溃之前能够给出一定的信息。
#pragma是非统一的,他要依靠各个编译器生产者,例如,在SUN C++编译器中:
char name[9];
double val;
// 把name和val的起始地址调整为8个字节的倍数
#progma align 8 (name, val)
//在程序执行开始,调用函数MyFunction
#progma init (MyFunction)
预定义标识符##
为了处理一些有用的信息,预处理定义了一些预处理标识符,虽然各种编译器的预处理标识符不尽相同,但是他们都会处理下面的4种:
__FILE__ // 正在编译的文件的名字
__LINE__ // 正在编译的文件的行号
__DATE__ // 编译时刻的日期字符串,例如: "25 Dec 2000"
__TIME__ // 编译时刻的时间字符串,例如: "12:30:55"
例如:
cout<<"The file is :"<<__FILE__"<<"! The lines is:"<<__LINE__<<endl;
defined 预处理运算符:
defined NAME 是用来判断NAME是否被定义了(被用define定义了)。
#ifdef NAME == #if defined(NAME)
#ifndef NAME == #if !defined(NAME)
但是在处理双重和多重判断时,defined的优势就显示出来了
#ifdef NAME1
#ifdef NAME2
// ......
#endif
#endif
可以写成
#if defined(NAME1) && defined(NAME2)
// ......
#endif
预处理何去何从##
C++并没有为 #include 提供替代形式,但是 namespace 提供了一种作用域机制,它能以某种方式支持组合,利用它可以改善 #include 的行为方式,但是我们还是无法取代 #include。
#progma 应该算是一个可有可无的预处理指令,按照 C++ 之父 Bjarne 的话说,就是:" #progma 被过分的经常的用于将语言语义的变形隐藏到编译系统里,或者被用于提供带有特殊语义和笨拙语法的语言扩充。”
对于 #ifdef,我们仍然束手无策,就算是我们利用 if 语句和常量表达式,仍然不足以替代她,因为一个 if 语句的正文必须在语法上正确,满足类检查,即使他处在一个绝不会被执行的分支里面。