Home C&C++函数库 c++ 语法 程序源码 Linux C库

预处理指令


预处理器指令是程序代码中前面有#符号的行。这些行不是程序语句,而是预处理器的指令。 预处理器在实际开始编译代码之前检查代码,并在由常规语句实际生成任何代码之前解析所有这些指令

预处理器指令只允许一行代码。只要找到一个换行符,预处理器指令就结束。 预处理器指令的末尾不需要分号(;)。预处理器指令可以扩展到多行的唯一方法是 在换行符之前加上一个反斜杠(\)。

☞  宏定义(#define, #undef)



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

#define identifier replacement

当预处理器遇到此指令时,它将替换其余代码中出现的任何标识符。 这个替换可以是一个表达式、一条语句、一个块或简单的任何东西。 预处理器不能理解c++语句,它只是用替换替换任何标识符。

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

已定义的宏不受块结构的影响。一个宏会一直持续到使用#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);

这将转化为:

cout << "test";

操作符##将两个实参连接起来,其间没有空格:

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

这可转换为:

cout << "test";

因为预处理器替换发生在任何c++语法检查之前,所以宏定义可能是一个难以捉摸的特性。 但是,要注意:严重依赖复杂宏的代码可读性会降低,因为在许多情况下, 所期望的语法与程序员在c++中期望的普通表达式不同。



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



如果满足某个条件,这些指令允许包含或忽略程序代码的一部分。

#ifdef允许程序的某个部分只有在指定为形参的宏已经定义的情况下才被编译, 而不管它的值是什么。例如:

#ifdef TABLE_SIZE
int table[TABLE_SIZE];
#endif 

在本例中,代码行int table[TABLE_SIZE];只有在TABLE_SIZE之前用#define定义过的情况 下才会编译,而与TABLE_SIZE的值无关。如果未定义,则该行将不包含在程序编译中。

#ifndef的作用正好相反:在#ifndef和#endif指令之间的代码只有在指定的标识符没有被预先 定义时才会被编译。例如:

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

在本例中,如果到达这段代码时,TABLE_SIZE宏还没有定义,那么它将被定义为100。 如果它已经存在,它将保持原来的值,因为#define指令不会被执行。

#if, #else和#elif(即"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指令中分别定义和定义的特殊操作符来实现:

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

这段代码将生成一个错误,在文件“赋值变量”第20行中显示为错误。

☞  错误指令(#error)



这个指令在找到它时中止编译过程,生成一个编译错误,可以指定为它的参数:

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

如果未定义宏名__cplusplus(此宏名在所有c++编译器中默认定义),则该示例将中止编译过程。

☞  源文件包含(#include)



这个指令在本教程的其他部分已经被认真地使用了。当预处理器找到一个#include指令时, 它会用指定头文件或文件的全部内容替换它。#include有两种使用方法:

#include <header>
#include "file" 

在第一种情况下,头文件在尖括号<> 之间指定。这用于包含实现提供的头文件, 比如组成标准库的头文件(iostream, string,…)。 头文件是否真的是文件或以其他形式存在是由实现定义的,但在任何情况下, 它们都应该被正确地包含在这个指令中。

第二个#include中使用的语法使用引号,并包含一个文件。该文件搜索它的实现和定义,通常包括当前路径。 在没有找到文件的情况下,编译器将该指令解释为头文件包含,就像尖括号(<>)一样。

☞  pragma指令(#pragma)



该指令用于向编译器指定不同的选项。这些选项是特定于您使用的平台和编译器的。 有关可以使用#pragma定义的可能参数的更多信息,请参阅编译器的手册或参考资料。

如果编译器不支持#pragma的特定参数,它将被忽略——不会生成语法错误。

☞  预定义的宏



下面的宏名称总是被定义(它们都以两个下划线字符_开始和结束):

宏指令
__LINE__ 表示正在编译的源代码文件中的当前行的整数值。
__FILE__ 被编译的源文件的假定名称的字符串字面值。
__DATE__ 格式为“Mmm dd yyyy”的字符串字面值,其中包含编译过程开始的日期。
__TIME__ 格式为“hh:mm:ss”的字符串字面值,包含编译过程开始的时间。
__cplusplus 一个整数值。所有c++编译器都将这个常量定义为某个值。 它的值取决于编译器支持的标准版本:
  • 199711L: ISO C++ 1998/2003
  • 201103L: ISO C++ 2011
不符合标准的编译器将此常量定义为长度不超过5位的值。请注意, 许多编译器并没有完全遵循这一规则,因此将该常量定义为上述两个值中的任何一个。
__STDC_HOSTED__ 1 如果实现是hosted implementation(具有所有标准头)
0 相反

下面的宏是可选定义的,通常取决于一个特性是否可用:

宏指令
__STDC__ 在C语言中:如果定义为1,则实现符合C语言标准。
在c++中:实现定义。
__STDC_VERSION__ 在C语言中:
  • 199401L: ISO C 1990, Ammendment 1
  • 199901L: ISO C 1999
  • 201112L: ISO C 2011
在c++中:实现定义。
__STDC_MB_MIGHT_NEQ_WC__ 1 如果多字节编码可能在字符字面值中给一个字符一个不同的值
__STDC_ISO_10646__ 格式为yyyymmL的值,指定Unicode标准的日期,然后是wchar_t字符的编码
__STDCPP_STRICT_POINTER_SAFETY__ 1 如果实现有严格的指针安全(参见get_pointer_safety)
__STDCPP_THREADS__ 1 如果程序可以有多个线程
特定的实现可以定义额外的常量。 例如:
// 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



联系我们 免责声明 关于CandCplus 网站地图
>