B.11.2 预处理程序指令

所有预处理程序指令都以字符#开头,它必须是命令所在行的第一个非空白字符。字符#之后可以有选择地跟随一个或多个空格或者制表符。

1.#define指令

格式1:


define name text


这个格式向预处理程序定义标识符名称,并将该名称与在name到其所在行结束间的第一个空格之后出现的任何text关联起来。随后,在程序中使用name时,将会直接代替程序相应位置的text。

格式2:


define name(param_1,param_2,……,param_n)text


这个格式定义一个宏指令name,它接受由param_1、param_2……param_n指定的参数,每个参数是一个标识符。随后,在程序中使用带有参数列表的name时,将直接替换程序相应位置的text,并用宏指令调用的参数替换text中出现的所有对应参数。

如果该宏接受可变数目的参数,可以在参数列表结尾使用3个圆点。列表中其余参数将通过特殊标识符VA_ARGS在宏指令定义中集体引用。作为一个例子,下列语句定义一个名为myPrintf的宏,它接受一个后面跟有可变数目参数的前导格式字符串:


define myPrintf(……)printf(“DEBUG:VA_ARGS);


合法的宏用途包括


myPrintf(“Hello world!\n”);


以及


myPrintf(“i=%i, j=%i\n”,i, j);


如果定义需要多行,必须以斜杠字符结束以延续每一行。定义一个名称之后,该名称就可以用于文件中的任何位置。

运算符#允许用在以下#define指令中,这些指令接受参数并且之后是该宏的参数名称。调用宏时,传递给宏指令的实际值时,预处理程序将实际值放在双引号中。换言之,预处理程序将把这个值转换为一个字符串。例如,使用调用


define printint(x)printf(#x“%d\n”,x)


的定义


printint(count);


将被预处理程序扩展成


printf(“count”“%i\n”,count);


或者,等价于


printf(“count=%i\n”,count);


执行字符串化操作时,预处理程序将在任何或\字符之前放置\字符。因此,使用声明


define str(x)#x


可以将调用


str(The string“t”contains a tab)


扩展成以下形式:


“The string\”\t\“contains a tab”


在接收参数的#define指令中还允许使用##运算符。它放在某个宏指令的参数名称之前(或者在它后面跟有一个宏指令的名称)。预处理程序使用在调用宏时所传递的值,并根据参数为宏指令创建一个标记,以及后面(或前面)的标记。例如,使用调用


printx(5)


的宏指令定义


define printx(n)printf(“i\n”,x##n);


会产生以下形式:


printf(“i\n”,x5);


使用调用


printx(10)


的宏指令定义


define printx(n)printf(“x”#n“%i\n”,x##n);


当替换并连接字符串之后,会产生


printf(“x10=%i\n”,x10);


和##字符两侧不允许出现空格。2.#error指令

一般格式:


error text

……


预处理程序将特定的text编写成一条出错消息。

3.#if指令

格式1:


if constant_expression

……

endif


这个格式首先计算constant_expression的值。如果结果非零,将处理#endif指令之前的所有程序行;否则,它们将被自动略过并且不被预处理程序或者编译器处理。

格式2:


if constant_expression_1

……

elif constant_expression_2

……

elif constant_expression_n

……

else

……

endif


如果constant_expression_1非零,将处理#elif之前的所有程序行,在其之后直到#endif间的其余程序行将被略过。否则,如果constant_expression_2非零,将处理下一个#elif之前的所有程序行,并略过它之后直到#endif之前的其余程序行。如果任何语句表达式的计算结果都非零,则处理#else之后的程序行(如果包括的话)。

特殊运算符defined可以用作常量表达式的一部分,因此对于


if defined(DEBUG)

……

endif


如果前面定义过标识符DEBUG(同时参见下一节定义的#ifdef),则处理#if和#endif之间的代码。标识符两侧不必使用圆括号,因此


if defined DEBUG


将运行良好。

4.#ifdef指令

一般格式:


ifdef identifier

……

endif


如果identifier的值在前面定义过(或者编译程序时通过#define定义或使用-D命令行选项定义的),将处理#endif之前的所有程序行;否则,这些程序行将被略过。就像使用#if指令一样,#elif和#else指令也可以和#ifdef指令一起使用。

5.#ifndef指令

一般格式:


ifndef identifier

……

endif


如果前面没有定义identifier的值,将处理#endif之前的所有程序行;否则,这些程序行将被略过。就像使用#if指令一样,#elif和#else指令也可以和#ifndef指令一起使用。

6.#import指令4

格式1:


import“fileName”


如果fileName指定的文件已经在前面包含到程序中,则这条语句将被略过。否则,预处理程序将搜索实现定义的目录(或多个目录)以查找fileName。通常,首先检索包含源文件的同一目录,如果没有发现该文件,将检索一系列实现定义的标准位置。找到这个文件之后,将在#import指令出现的确切位置将该文件的内容包含到程序中。然后分析包括到程序的文件中所含的预处理指令;因此,包括到程序中的文件本身也可以包含其他的#import或者#include指令。

格式2:


import<fileName>


如果前面没有包含这个文件,预处理程序只在标准位置检索指定的文件。特别地,搜索忽略了当前源文件。这一操作将在找到该文件之后执行,否则,与前面所描述的完全一样。

其他格式中,可以提供前面定义的名称并且还会出现扩展名。因此,下列序列也会起作用:

define ROOTOBJECT<NSObject.h>

……

import ROOTOBJECT


7.#include指令</p>

除了不对前面包含的指定头文件进行检查之外,这个指令与#import具有相同的功能。

8.#line指令

一般格式:


line constant“fileName”


这个指令会使编译器处理程序中随后的程序行,就像源文件的名称为fileName且随后程序行的行编号以constant开始一样。如果没有指定fileName,文件名将由最后的#line指令指定,或使用源文件的文件名(如果前面没有指定文件名)。

line指令主要用于在显示编译器发出的出错消息时控制文件名和行编号。

9.#pragma指令

一般格式:


pragma text


这个指令会使预处理程序执行一些实现定义的操作。例如,以下附注


pragma loop_opt(on)


会在特定的编译器上执行特殊的循环优化。如果编译器遇到这个附注,该编译器识别不了附注loop_opt,就会忽略它。

10.#undef指令

一般格式:


undef identifier


对于预处理程序而言,指定的identifier将成为未定义的。随后的#ifdef或者#ifndef指令在使用中会认为这个标识符是从未定义过的。

11.#指令

这个一条空指令,将被预处理程序忽略。