【问题标题】:Restricting preprocessing-numbers in a C preprocessor to only handle valid floating and integer constants将 C 预处理器中的预处理数字限制为仅处理有效的浮点和整数常量
【发布时间】:2021-09-30 21:23:51
【问题描述】:

我目前正在实现 C11 编译器,我的目标是将预处理器集成到其余编译器中,而不是将其作为独立组件。因此,预处理器可以安全地假设其输出将在以下阶段有效。

阅读有关预处理数字令牌的信息,似乎它只是为了简化独立预处理器的实现而存在。简化数字的格式,它不必处理数字表达式的全部复杂性。引用GCC docs

这个不寻常的定义的目的是将预处理器与数字常量的全部复杂性隔离开来。它不必区分词法有效和无效的浮点数,这很复杂。

由于预处理器将集成到编译器框架的其余部分,这对我来说不是问题。

在第 6.4.8.4 节[预处理数字;语义] C11 标准,它声称

预处理数字没有类型或值;它在成功转换(作为转换阶段 7 的一部分)到浮点常量令牌或整数常量令牌后获取两者。

因此,似乎每个预处理数字都会在稍后的编译过程中转换为浮点或整数常量。我在标准中找不到任何其他对预处理编号的引用,所以这似乎是他们唯一的目的,但我可能错了。

我的问题是,预处理器将预处理数字限制为仅有效的整数和浮点常量是否有效?或者是否存在这样的限制会导致其他有效程序失败的情况?

【问题讨论】:

  • 哪些 PP 数字不是有效的整数或浮点常量?
  • this ? The program fragment 1Ex is parsed as a preprocessing number token (one that is not a valid floating or integer constant token), 但这很奇怪,因为在 pp-number syntax 中,符号 not 是可选的。
  • @Barmar 0x2x334.2.8 是有效的 pp 数字,但不是有效的整数或浮点常量。 pp-numbers 要轻松得多。
  • @KamilCuk 我猜“1Ex”可以解析为<digit> <identifier-nondigit> <identifier-nondigit>,因为预处理数字可能包含非数字标识符。但是,我认为1Ex 的示例并没有真正回答这个问题,如果预处理器将其解析为1(整数常量)Ex(标识符),那么结果在后面的阶段(整数不能紧跟标识符)。
  • 是的。所以关键是,如果“预处理器将预处理数字限制为仅有效的整数和浮点常量是有效的”,那么1Ex 将被不正确地解析。

标签: c syntax c-preprocessor standards


【解决方案1】:

当然有一些有效的程序包含不可转换为整数或浮点数的 pp 数。常见的情况是预处理令牌不会成为令牌。

例如,它可能是字符串化的:


#define STRINGIFY_(X) #X
#define STRINGIFY(V)  STRINGIFY_(V)
#define VERSION 3.4.6a
#define PROGNAME foo

int main(void) {
  printf("%s-%s\n", STRINGIFY(PROGNAME), STRINGIFY(VERSION));
}

此外,上面示例中的版本号可以通过令牌连接产生,另一种方式是预处理令牌永远不会成为程序令牌:


#include <stdio.h>
#define STRINGIFY_(X) #X
#define STRINGIFY(V)  STRINGIFY_(V)
#define CONCAT3_(x,y,z) x##y##z
#define CONCAT3(x,y,z) CONCAT3_(x,y,z)
#define CONCAT_V(mj, mn, pl) CONCAT3(mj, ., CONCAT3(mn, ., pl))

#define MAJOR 3
#define MINOR 4
#define PATCH 6a

#define VERSION CONCAT_V(MAJOR, MINOR, PATCH)
#define PROGNAME foo

int main(void) {
  printf("%s-%s\n", STRINGIFY(PROGNAME), STRINGIFY(VERSION));
}

还有其他方法可以使 pp-number(或任何其他预处理令牌)永远不会转换为令牌:

  1. 作为在其替换文本中不使用相应参数的宏的参数。

  2. 在控制表达式为假的预处理器条件中的程序文​​本中。

    这通常用于“野外”隐藏#if 0 ... #endif 块中未完全编写的代码;只要 cmets 和字符串被终止,被排除的代码可能有几乎任意的语法错误,包括无效的 pp-numbers 甚至是杂散的标点符号。 (@ 是一个有效的预处理令牌,不能转换为令牌。)

【讨论】:

  • 啊,谢谢,我没有考虑串联和字符串化,但这绝对是真的。
  • @Loovjo 这真的有问题吗?如果您将它们解析为标记而不是 pp 编号,您仍然可以将它们字符串化。
  • @Barmar:然后考虑 pp-number 0x 与标识符 DEADBEEF 的令牌连接。串联是一个有效数字。 (或者,就此而言,0x123CAD 的串联。)
  • @loovjo:顺便说一下,我一直在标准中读到这句话的意思是,如果数字的(复杂的)语法发生了变化,预​​处理器仍然可以工作。但事实证明,这些更改需要在预处理器中进行更改,例如为“可以后跟符号的字母”添加另一种情况,或者对 C++ 进行的极其不幸(恕我直言)的更改,它允许 ' 成为用作千位分隔符。 (_ 不需要在预处理器中进行更改,并且工具更改比必须弄清楚撇号何时不是引号更简单。)
猜你喜欢
  • 2023-03-17
  • 2011-06-09
  • 1970-01-01
  • 1970-01-01
  • 2011-04-26
  • 2011-01-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多