c++教程(二十四:Preprocessor directives)

————————————————————————

该系列教程为翻译c++官方教程,点击参考英文原版,水平有限,翻译不通之处敬请谅解!

————————————————————————

Preprocessor directives(预处理指令)

预处理包含在代码的程序之前,有一个哈希标志(#)。这些线不是程序语句但是是预处理指令。预处理器的汇编代码实际开始编译代码之前是解决所有这些指令,任何的代码实际上是通过固定的语句生成。

这些预处理指令只能在一个单一的代码行中。当遇到一个换行符,预处理器指令结束。预处理器指令结束没有分号(;)。预处理器指令可以延长到超过一行的唯一方法就是前面的换行符在行尾的反斜杠(\)。

宏定义(#define,# undef)

定义预处理器宏可以用#define。它的语法是:

#define identifier replacement

当处理器遇到这个指令,它在剩下的代码中identifier 就被replacement代替,替换成后面的。这个replacement 可以是表达式、语句、块或简单的任何东西。预处理器不能准确理解C++,它只是简单地用replacement取代identifier 。

#define TABLE_SIZE 100
int table1[TABLE_SIZE];
int table2[TABLE_SIZE];

经过预处理器取代了table_size,代码变得相当于:

int table1[100];
int table2[100];

# define 也可以用参数来定义函数:

#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;
}

定义的宏不受块结构的影响。宏直到它的未定义 undef预处理指令为止:

#define TABLE_SIZE 100
int table1[TABLE_SIZE];
#undef TABLE_SIZE
#define TABLE_SIZE 200
int table2[TABLE_SIZE];

上述代码相当于:

int table1[100];
int table2[200];

函数的宏定义在置换序列接受两个特殊操作符(#和##):
操作符#,后面为一个参数的名称,是由一个包含该参数字符串替换了(如果包含在双引号):

#define str(x) #x
cout << str(test);

可以理解为:

扫描二维码关注公众号,回复: 3429771 查看本文章
cout << "test";

操作符##需要两个参数,并且它们之间没有空白:

#define glue(a,b) a ## b
glue(c,out) << "test";

可以等效为:

cout << "test";

因为预处理器在C++任何语法检查都会发生替换,宏定义可以是一个棘手的特征。但是,要注意的是:很大程度上依赖于复杂的宏可能使得代码不可读,因为这样使得语法在C++许多场合中不同于正常表达。

条件包含(Conditional inclusions)(#ifdef, #ifndef, #if, #endif, #else and #elif)

这些指令如果满足某一条件允许包含或丢弃程序的部分代码。
# ifdef允许只有宏指定为参数程序的一个部分是进行编译,不管它的值。例如:

#ifdef TABLE_SIZE
int table[TABLE_SIZE];
#endif 

在这种情况下,代码int table [ table_size ],只有table_size先通过#定义,才能被编译。如果未定义,则该行将不包含在程序编译中。
#ifndef 则是完全相反的:如果指定的标识符之前还未定义,那么# ifndef和# endif指令之间的代码只编译。例如:

#ifndef TABLE_SIZE
#define TABLE_SIZE 100
#endif
int table[TABLE_SIZE];

在这种情况下,如果到了这段代码,TABLE_SIZE 宏尚未被定义,它就被定义为值100。如果它已经存在,它将保持其先前的值,#指令不被执行。

其他的#if, #else and #elif (i.e., “else if”) (即“如果”)指令为指定要满足的部分代码被编译的一些条件。条件#if或# elif只能是常量表达式,包括宏观的表达。例如:

#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相结合使用的。

对# ifdef和##ifndef还可以通过使用特殊的运营商定义的实现!分别在任何#if或#elif处进行defined :

#if defined ARRAY_SIZE
#define TABLE_SIZE ARRAY_SIZE
#elif !defined BUFFER_SIZE
#define TABLE_SIZE 128
#else
#define TABLE_SIZE BUFFER_SIZE
#endif 

线控制(#line)

编译程序时,编译过程中会出现一些错误,编译器会显示一个错误信息,其中引用了错误发生的文件名和行号,因此很容易找到产生错误的代码。

#line指令允许我们控制这些事情,包括行号的代码信息,以及我们希望文件名时出现一个错误发生位置。其格式是:

#line number "filename"

这里number 被分配到下一个代码行的新行号。连续行的行号将从这一点上逐点增加。
“filename”是一个可选参数,允许重新定义要显示的文件名。例如:

#line 20 "assigning variable"
int a?;

此代码将生成一个错误,将显示为错误的文件“assigning variable”,第20行。

错误指令(#error)

这个指令发生的时候中止编译过程,产生一个编译错误,可以指定为参数:

#ifndef __cplusplus
#error A C++ compiler is required!
#endif 

本例如果没有定义的宏名__cplusplus中止编译过程,(宏的名字是在所有的C++编译器默认的定义)。

源文件包含(#include)

这个指令已经在本教程的其他部分经常出现,一个#include预处理器指令可以取代它指定特定头文件的全部内容。有两种方式使用#include:

#include <header>
#include "file"

在第一种情况下,在角括号之间指定header。这是用于包括安装的头文件,如标准库头文件(iostream,string,…)。头文件是否为真实的文件或存在于其他位置是implementation-defined,在任何情况下,它们都应正确地包含在这个指令中。

在第二个 #include 使用引号,并包括一个文件。该文件以搜索的方式执行定义,该文件通常包括在当前路径。在未找到文件的情况下,编译器将指令解释为包含头<>的内容,就像引号(“”)被“<>”替换为。

pragma指令(# Pragma)

此指令用于指定编译器的不同选项。这些选项是指你使用的特定的平台和编译器。参考相关手册或编译器的更多信息,可以得到#pragma的更多用法参考。

如果编译器不支持#pragma的具体参数,它会被忽略,不产生语法错误。

预定义的宏名称

下面的宏的名字定义为(他们都开始和结束于两个下划线,_):
这里写图片描述

下列宏可任意定义,通常取决于是否有可用的功能:

这里写图片描述

特定实现可以定义附加常数。
例如:

// 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

猜你喜欢

转载自blog.csdn.net/on2way/article/details/54916178