当前位置: C语言 -- 基础 -- 预处理指令

预处理指令(一)

一、概述

实现能够有条件地处理或者跳过源文件的某些部分、包含其它源文件以及替换宏,这称为预处理(preprocessing)。从概念上讲预处理发生在编译单元编译之前。预处理指令(preprocessing directive)由预处理标记(preprocessing tokens)序列构成,预处理标记应满足以下要求:

-- 序列的第一个标记是#预处理标记,该标记在编译阶段步骤4开始时必须是源文件的第一个字符(前面可以有空格,但不能有换行。),或者必须跟在包含至少一个换行符的空白序列之后。

-- 序列中的最后一个标记是序列中第一个标记后的第一个换行符。

-- 换行符会终止预处理指令,即使换行符出现在类似函数式宏(function-like macro)的调用过程中。

#include <stdio.h>	//预处理指令。

#define N 10		//预处理指令。

文本行(即非指令行)不能以#预处理标记开头;非指令不能以任何指令名(directive names)开头。

ISO/IEC 9899:2024标准中指令名包括:ififdefifndefelifelifdefelifndefelseendifincludeembeddefineundeflineerrorwarningpragma。)


ISO/IEC 9899:2024标准对预处理指令作了一些限制:

-- 预处理指令中(从#开始到换行符结束),预处理标记间只能出现两种空格字符(white-space characters):空格符和水平制表符;空格符包括编译阶段步骤3中替换注释或者其它空格字符的空格。

C语言中标准空格字符包括空格符(' ')、换页符('\f')、换行符('\n')、回车符('\r')、水平制表符('\t')和垂直制表符('\v')。)


-- 预处理器形参只能是以下两种情况之一:预处理器标准形参或者实现定义的预处理器前缀形参。

#embed "gch.bin" limit(8)       //limit是预处理器标准形参。           
#embed "gch.bin" gnu::offset(8) //gnu::offset是预处理器前缀形参,用于GCC编译器。

预处理器标准形参(preprocessor standard parameter)由ISO标准定义;预处理器前缀形参(preprocessor prefixed parameter)由实现定义。


-- 无法识别的预处理器前缀形参是约束违规,但如果在has_embed表达式内除外。

//如果gnu::offsets预处理器前缀形参不存在,
//表达式__has_embed( "gch.bin" gnu::offsets(8) )值会评估为0;
//但这里预处理器前缀形参无法识别并不会约束违规。
#if __has_embed( "gch.bin" gnu::offsets(8) )  //等价于#if 0。

作为预处理器形参使用时,除了拼写差异外,预处理器标准形参(标识符格式为pp_param)和格式为__pp_param__的标识符应表现相同。

//以下两条预处理指令是等价的。
#embed "gch.bin" limit(8)
#embed "gch.bin" __limit__(8)

当处于条件编译被跳过的代码块时(即处于条件编译不被编译的代码块时),预处理指令的语法限制会被放宽,允许在指令名和换行符之间出现任何预处理标记序列。

#if 0

//#include abcd不是合法的源文件包含指令;
//但该代码会被跳过,不会被编译;
//因此整个代码块仍然是合法的。
#include abcd

#endif

除非另有明确说明,否则预处理指令中的预处理标记不会进行宏扩展(macro expansion)。

#define WHITESPACE
WHITESPACE #define SIZE 100

第二行预处理标记序列不构成预处理指令,因为在编译阶段步骤4开始时它不是以#开头,即使宏WHITESPACE替换后该行会以#开头。执行非指令形式的预处理指令将导致未定义行为。