- B.11.2 预处理程序指令
- define name text
- define name(param_1,param_2,……,param_n)text
- define myPrintf(……)printf(“DEBUG:VA_ARGS);
- define printint(x)printf(#x“%d\n”,x)
- define str(x)#x
- define printx(n)printf(“i\n”,x##n);
- define printx(n)printf(“x”#n“%i\n”,x##n);
- 和##字符两侧不允许出现空格。2.#error指令
- error text
- if constant_expression
- endif
- if constant_expression_1
- elif constant_expression_2
- elif constant_expression_n
- else
- endif
- if defined(DEBUG)
- endif
- if defined DEBUG
- ifdef identifier
- endif
- ifndef identifier
- endif
- import“fileName”
- import<fileName>
- define ROOTOBJECT<NSObject.h>
- import ROOTOBJECT
- line constant“fileName”
- line指令主要用于在显示编译器发出的出错消息时控制文件名和行编号。
- pragma text
- pragma loop_opt(on)
- undef identifier
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.#指令
这个一条空指令,将被预处理程序忽略。