C预处理器-1(CPP -C PreProcessor)

C 预处理器实现了一种宏语言,这种宏语言用于在 C、C++ 和 Objective - C 程序编译之前对它们进行转换。它自身也有独立的用途。

1 概览

C 预处理器,通常被称为 “cpp”,是一种 “宏处理器”。在编译之前,C 编译器会自动使用它来转换你的程序。之所以称它为宏处理器,是因为它允许你定义 “宏”,而宏是较长结构的简短缩写形式。

C 预处理器旨在仅用于处理 C、C++ 和 Objective-C 源代码。过去,它曾被滥用来作为通用的文本处理器。但对于不符合 C 语言词法规则的输入,它将无法正确处理。例如,单引号会被解释为字符常量的开头,从而导致错误。此外,不能依赖它保留对 C 语言家族不重要的输入特性。如果一个 Makefile 被预处理,所有的硬制表符(Tab)都会被移除,这将导致 Makefile 无法正常工作。

话虽如此,你经常可以侥幸地将 cpp(C 预处理器)用于非 C 语言的文件。其他类似 Algol 的编程语言(如 Ada 等)通常也是安全的。汇编语言也可以,但需要小心处理。在 -traditional-cpp 模式下,预处理器会保留更多的空白字符,并且在其他方面更加宽容。许多问题可以通过以下方式避免:使用 C 或 C++ 风格的注释而不是原生语言的注释,并保持宏定义的简单性。

无论何时,只要可能的话,你应当使用针对你所编写的语言设计的预处理器。现代版本的 GNU 汇编器就包含了宏功能。大多数高级编程语言都有自己的条件编译和包含机制。如果所有其他方法都不可行,那么可以尝试使用真正的通用文本处理器,例如 GNU M4。

【这种做法的好处是,专门为某种语言设计的预处理器能够更好地理解该语言的语法和结构,从而更有效地处理代码中的宏定义、条件编译等功能,同时减少错误和不期望的行为。对于特定语言使用正确的工具,不仅可以提高工作效率,还能增强代码的可维护性和清晰度。】

C 预处理器在某些细节上有所不同。此手册讨论的是 GNU C 预处理器,它提供了 ISO 标准 C 特性的一个小超集。在默认模式下,GNU C 预处理器不会执行标准要求的一些很少使用、甚至几乎不使用的功能,因为这些功能可能会导致未预期的程序行为变化。如果你想严格遵循 ISO 标准 C,应该根据所需的标准版本选择使用 -std=c90-std=c99-std=c11-std=c17 选项。此外,若要获取所有强制性诊断信息,你还必须使用 -pedantic 选项。这确保了你的代码符合所选标准的所有要求和规范。

本手册描述了 ISO 标准预处理器的行为。为了尽量减少不必要的差异,在 ISO 预处理器的行为不与传统语义冲突的情况下,传统预处理器应表现出相同的行为。各种现存的差异在 传统模式(Traditional Mode)章节中有详细说明。

为明确起见,除非另有说明,本手册中对 CPP 的引用均指 GNU CPP(GNU C 预处理器)。

1.1 字符集Character sets

C 语言及其相关语言中的源代码字符集处理相当复杂。C 标准提到了两种字符集,但实际上至少涉及四种。

输入到 C 预处理器(CPP)的文件可能采用任何字符集。CPP 在处理文件时,其首要操作甚至在查找行边界之前,就是将文件转换成用于内部处理的字符集。该字符集即 C 标准所称的源字符集(Source Character Set)。它必须与 ISO 10646(也就是 Unicode)同构。CPP 使用的是 Unicode 的 UTF-8 编码。

输入文件的字符集可以通过 -finput-charset= 选项来指定。

【这意味着,如果你的源代码文件使用了不同于 UTF-8 的编码方式(例如,ISO-8859-1 或 GBK),你可以通过此选项告知 CPP 如何正确解读这些文件。例如,若你的源文件是用 ISO-8859-1 编码的,可以使用 -finput-charset=ISO-8859-1 来确保 CPP 正确地将其转换为 UTF-8 进行内部处理。这样即使源文件采用了不同的编码格式,也能保证预处理过程中的兼容性和准确性。】

所有的预处理工作(本手册其余部分的主题)都是在源字符集(Source Character Set)中进行的。如果你使用 -E 选项从预处理器请求文本输出,该输出将会是 UTF-8 编码格式。

预处理完成后,字符串和字符常量会被再次转换到执行字符集(Execution Character Set)。这个字符集由用户控制,默认情况下与源字符集相同,即 UTF-8。宽字符串和字符常量有其自己的字符集,标准中并未特别指出这一字符集,同样由用户控制,默认为 UTF-16 或 UTF-32,具体取决于目标机器 wchar_t 类型能够容纳哪种格式及其字节序。

  • 八进制和十六进制转义序列不会被转换;例如,'\x12' 的值始终为 0x12,不受当前选择的执行字符集影响。
  • 其他所有转义字符首先会被替换为源字符集中它们所代表的字符,然后像未转义的字符一样被转换到执行字符集。

在标识符中,可以使用 \u\U 转义序列指定 ASCII 范围之外的字符,或者直接在输入编码中使用这些字符。如果通过诸如 -std=c90-fno-extended-identifiers 等选项指定了严格的 ISO C90 标准一致性,则不允许在标识符中使用这些构造。

这意味着,在编写符合特定标准的代码时,需要注意使用的字符集以及如何正确地处理不同类型的字符常量和字符串,尤其是在需要跨平台或支持多语言的情况下。此外,了解如何使用不同的编译器选项来控制这些行为对于确保代码的兼容性和正确性至关重要。

UTF-16 不符合 C 标准对宽字符集的要求,但 16 位 wchar_t 的选择在某些系统 ABI (Application Binary Interface,应用程序二进制接口)中已经被确立,因此我们无法解决这个问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值