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

预处理指令(十一)

七、诊断指令

C语言中存在以下两种诊断指令(diagnostic directives):

#error pp-tokensopt new-line
#warning pp-tokensopt new-line

其中errorwarning是指令名;pp-tokens是预处理标记;new-line是换行符。


#error指令和#warning指令会使实现生成一条诊断信息,该信息包含指定的预处理标记序列。诊断指令可用于平台检查、编译器版本检查、废弃代码和功能标记等。

#ifndef __unix__
  #error "The code is only compiled on an unix system!"
#endif

#if __STDC_VERSION__ < 202311L
  #warning "The code is only compiled with C23 or later!"
#endif 

八、空指令

空指令(null directive)具有以下语法格式:

# new-line

其中new-line是换行符。

空指令不执行任何实际操作,没有任何效果;主要用途是占位,满足语法结构要求。


九、pragma指令

#pragma预处理指令具有以下语法格式:

#pragma pp-tokensopt new-line

其中pragma是指令名;pp-tokens是预处理标记;new-line是换行符。


如果#pragma指令中pragma指令名后没有紧跟STDC预处理标记(宏替换前。),#pragma指令将以实现定义的方式运行。这可能导致编译失败、编译器行为或者最终程序行为不符合ISO标准。

//该#pragma指令适用于GCC编译器。
//该指令检查当前文件与另一个文件的相对日期。
//如果另一个文件较当前文件更新,则发出警告。
#pragma GCC dependency "test.cpp"

如果#pragma指令中pragma指令名后紧跟STDC预处理标记(宏替换前。),该#pragma指令不会进行任何宏替换,这样的#pragma指令称为标准#pragma指令。标准#pragma指令包括:

//该编译提示表示是否允许实现收缩表达式(contract expressions)。
//on表示允许;off表示禁止。
//on-off-switch值应是ON、OFF、DEFAULT三者之一。
#pragma STDC FP_CONTRACT on-off-switch

//该编译提示的目的是允许某些可能干扰浮点状态测试和模式更改的优化。
//off表示使用默认的浮点舍入模式。
//on表示程序可以访问浮点环境。
//on-off-switch值应是ON、OFF、DEFAULT三者之一。
#pragma STDC FENV_ACCESS on-off-switch

//该编译提示提供了一种机制,为十进制浮点类型的浮点操作指定舍入方向。
//dec-direction值应是FE_DEC_DOWNWARD、FE_DEC_TONEAREST、
//FE_DEC_TONEARESTFROMZERO、FE_DEC_TOWARDZERO、FE_DEC_UPWARD、FE_DEC_DYNAMIC之一。
#pragma STDC FENV_DEC_ROUND dec-direction

//该编译提示提供了一种机制,为标准浮点类型的浮点操作指定舍入方向。
//direction值应是FE_DOWNWARD、FE_TONEAREST、
//FE_TONEARESTFROMZERO、FE_TOWARDZERO、FE_UPWARD、FE_DYNAMIC之一。
#pragma STDC FENV_ROUND direction

//该编译提示表示是否可以使用常用数学公式。
//on表示可以使用常用数学公式;off表示不可以使用常用数学公式。
//on-off-switch值应是ON、OFF、DEFAULT三者之一。
#pragma STDC CX_LIMITED_RANGE on-off-switch

其中pragma是指令名;STDC表示该#pragma指令是ISO C标准定义的;FP_CONTRACT表示浮点收缩(浮点收缩是指编译器将多个离散的浮点运算合并成一条单一的、等效的机器指令的优化过程。);on-off-switch是开关状态;FENV_ACCESS表示浮点环境访问;FENV_DEC_ROUND表示十进制浮点环境舍入;dec-direction表示十进制浮点环境舍入方向;FENV_ROUND表示标准浮点环境舍入;direction表示标准浮点环境舍入方向;CX_LIMITED_RANGE是复数有限范围。


ISO标准不要求实现在#pragma指令中执行宏替换,但允许这样做(标准#pragma指令除外。)。如果非标准#pragma指令宏替换结果与标准#pragma指令具有相同形式,其行为仍由实现定义;实现可以将其视为标准#pragma指令,也可以不视为标准#pragma指令。

实现不能识别的#pragma指令(编译提示)将被忽略;但ISO标准鼓励实现对未识别的编译提示进行诊断。


十、pragma运算符

Pragma一元运算符具有以下语法格式:

_Pragma( string-literal )

其中_Pragma是预处理运算符;string-literal是字符串字面量。


Pragma表达式按以下步骤处理:

① 字符串字面量去字符串化。

字符串字面量去字符串化包括删除字符串字面量的编码前缀,删除字符串字面量的首尾双引号,转义序列\"使用"替换,转义序列\\使用\替换。

② 结果字符序列经过编译阶段步骤3的处理生成预处理标记,这些预处理标记像是#pragma指令中的预处理标记一样执行。

③ 删除表达式中的四个原始预处理标记。

//以下表达式可表示为指令:#pragma GCC dependency "test.cpp"
_Pragma ( "GCC dependency \"test.cpp\"" )

#pragma_Pragma的主要区别:#pragma是预处理指令,应满足预处理指令的要求;_Pragma是运算符,使用相对灵活,甚至可以在宏定义中使用,而#pragma不能。

#define PRAGMA(x) _Pragma(#x)

PRAGMA(GCC dependency "test.cpp")