预处理器指令
预处理器指令是指程序代码中包含的行,它们不是程序语句,而是给预处理器的指令。这些行总是以井号(
##)开头。预处理器在实际编译代码开始之前执行,因此预处理器会在生成任何代码之前处理所有这些指令。
这些预处理器指令仅扩展到一行代码。一旦找到换行符,预处理器指令就被认为结束。预处理器指令的末尾不需要分号(;)。预处理器指令可以通过在行尾的换行符前加上反斜杠(
\).
宏定义 (#define, #undef)
要定义预处理器宏,我们可以使用
#define。它的格式是
#define 标识符 替换内容
当预处理器遇到此指令时,它会将代码其余部分中任何出现的
标识符替换为
替换内容。这个
替换内容可以是表达式、语句、代码块或任何东西。预处理器不理解 C++,它只是简单地替换任何出现的
标识符为
替换内容.
1 2 3
|
#define TABLE_SIZE 100
int table1[TABLE_SIZE];
int table2[TABLE_SIZE];
|
在预处理器替换了
TABLE_SIZE之后,代码等效于
1 2
|
int table1[100];
int table2[100];
|
我们已经从之前的教程中了解到使用 #define 作为常量定义器,但是
#define也可以使用参数来定义函数宏
1
|
#define getmax(a,b) a>b?a:b
|
这将替换任何出现的
getmax,后跟两个参数,替换为替换表达式,并且还将每个参数替换为其标识符,就像您期望它是一个函数一样
|
// function macro
#include <iostream>
using namespace std;
#define getmax(a,b) ((a)>(b)?(a):(b))
int main()
{
int x=5, y;
y= getmax(x,2);
cout << y << endl;
cout << getmax(7,x) << endl;
return 0;
}
|
5
7 |
已定义的宏不受块结构的影响。宏一直有效,直到它被 #undef 预处理器指令取消定义
1 2 3 4 5
|
#define TABLE_SIZE 100
int table1[TABLE_SIZE];
#undef TABLE_SIZE
#define TABLE_SIZE 200
int table2[TABLE_SIZE];
|
这将生成与以下代码相同的代码
1 2
|
int table1[100];
int table2[200];
|
函数宏定义接受替换序列中的两个特殊运算符(
#和
### 和 ##)
如果在替换序列中使用参数之前使用了运算符
##,则该参数将被字符串文字替换(就像它被双引号括起来一样)
1 2
|
#define str(x) #x
cout << str(test);
|
这将转换为
运算符
#### 连接两个参数,它们之间不留空格
1 2
|
#define glue(a,b) a ## b
glue(c,out) << "test";
|
这也会被翻译成
因为预处理器替换发生在任何 C++ 语法检查之前,所以宏定义可能是一个棘手的功能,但请小心:严重依赖复杂宏的代码对于其他程序员来说可能看起来很晦涩难懂,因为他们期望的语法在很多情况下与程序员在 C++ 中期望的正则表达式不同。
条件包含 (#ifdef, #ifndef, #if, #endif, #else 和 #elif)
这些指令允许在满足特定条件时包含或丢弃程序的部分代码。
#ifdef允许仅在指定为参数的宏已定义时才编译程序的一部分,无论其值如何。例如
1 2 3
|
#ifdef TABLE_SIZE
int table[TABLE_SIZE];
#endif
|
在这种情况下,代码行
int table[TABLE_SIZE];只有在
TABLE_SIZE先前已使用
#define定义的情况下才会编译,与其值无关。如果未定义,则该行将不会包含在程序编译中。
#ifndef的作用正好相反:
#ifndef和
#endif指令之间的代码仅在指定的标识符之前未定义时才编译。例如
1 2 3 4
|
#ifndef TABLE_SIZE
#define TABLE_SIZE 100
#endif
int table[TABLE_SIZE];
|
在这种情况下,如果在到达这段代码时,
TABLE_SIZE宏尚未定义,则会将其定义为值 100。如果它已经存在,它将保留其先前的值,因为
#define指令将不会执行。
要放回的字符的
#if,
#else和
#elif(即“else if”)指令用于指定要满足的条件,以便编译它们所包围的代码部分。紧随其后的条件
#if#if
#elif或 #elif 只能评估常量表达式,包括宏表达式。例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
#if TABLE_SIZE>200
#undef TABLE_SIZE
#define TABLE_SIZE 200
#elif TABLE_SIZE<50
#undef TABLE_SIZE
#define TABLE_SIZE 50
#else
#undef TABLE_SIZE
#define TABLE_SIZE 100
#endif
int table[TABLE_SIZE];
|
请注意,
#if,
#elif和
#else链式指令的整个结构以
#endif.
#endif
#ifdef和
#ifndef结尾。
#ifdef和
!defined分别可以在任何
#if#if
#elif#if 指令中使用以达到相同效果。
1 2 3 4 5 6
|
#if !defined TABLE_SIZE
#define TABLE_SIZE 100
#elif defined ARRAY_SIZE
#define TABLE_SIZE ARRAY_SIZE
int table[TABLE_SIZE];
#endif
|
行控制 (#line)
当我们编译程序并且在编译过程中发生一些错误时,编译器会显示一条错误消息,其中引用了发生错误的文件的名称和行号,因此更容易找到生成错误的代码。
要放回的字符的
#line指令允许我们控制代码文件中的行号以及发生错误时要显示的文件名。它的格式是
#line number "filename"
其中
数字是分配给下一行代码的新行号。从这一点开始,连续行的行号将逐一递增。
"filename"是一个可选参数,允许重新定义将显示的文件名。例如
1 2
|
#line 20 "assigning variable"
int a?;
|
此代码将生成一个错误,该错误将显示为文件
"assigning variable"中的错误,第 20 行。
错误指令 (#error)
此指令在找到时中止编译过程,生成一个编译错误,该错误可以指定为其参数
1 2 3
|
#ifndef __cplusplus
#error A C++ compiler is required!
#endif
|
如果宏名称
__cplusplus未定义,此示例将中止编译过程(此宏名称默认在所有 C++ 编译器中定义)。
源文件包含 (#include)
本教程的其他部分也经常使用此指令。当预处理器找到
#include指令时,它会将其替换为指定文件的全部内容。有两种方法可以指定要包含的文件
1 2
|
#include "file"
#include <file>
|
两种表达式之间的唯一区别是编译器将查找文件的位置(目录)。在第一种情况下,文件名在双引号之间指定,首先在包含指令的文件的同一目录中搜索该文件。如果不存在,编译器将在配置为查找标准头文件的默认目录中搜索该文件。
如果文件名用尖括号括起来
<>,则直接在配置为查找标准头文件的位置搜索该文件。因此,标准头文件通常包含在尖括号中,而其他特定头文件使用引号包含。
Pragma 指令 (#pragma)
此指令用于为编译器指定各种选项。这些选项特定于您使用的平台和编译器。请参阅编译器的手册或参考,以获取有关可以使用
#pragma.
#pragma
#pragma的特定参数,则会忽略它 - 不会生成错误。
预定义的宏名称
以下宏名称在任何时候都已定义
宏 | 值 |
__LINE__ | 表示正在编译的源代码文件中的当前行的整数值。 |
__FILE__ | 一个字符串字面量,包含正在编译的源文件的假定名称。 |
__DATE__ | 格式为 "Mmm dd yyyy" 的字符串字面量,包含编译过程开始的日期。 |
__TIME__ | 格式为 "hh:mm:ss" 的字符串字面量,包含编译过程开始的时间。 |
__cplusplus | 一个整数值。所有 C++ 编译器都将此常量定义为某个值。如果编译器完全符合 C++ 标准,则其值等于或大于 199711L,具体取决于它们符合的标准版本。 |
例如:
|
// standard macro names
#include <iostream>
using namespace std;
int main()
{
cout << "This is the line number " << __LINE__;
cout << " of file " << __FILE__ << ".\n";
cout << "Its compilation began " << __DATE__;
cout << " at " << __TIME__ << ".\n";
cout << "The compiler gives a __cplusplus value of " << __cplusplus;
return 0;
}
|
This is the line number 7 of file /home/jay/stdmacronames.cpp.
Its compilation began Nov 1 2005 at 10:12:29.
The compiler gives a __cplusplus value of 1 |