【问题标题】:Stringize operator failure字符串化运算符失败
【发布时间】:2013-06-29 06:28:33
【问题描述】:

C 和 C++ 标准都包含文本,大意是如果字符串化操作未能生成有效的字符串文字标记,则行为未定义。在 C++11 中,这实际上是可能的,方法是在原始字符串文字中包含换行符。但标准中始终存在包罗万象的内容。

字符串化是否有其他方式可以产生 UB,而 UB 或格式错误的程序尚未发生?

我很想听听C 或C++ 的任何方言。我是writing 预处理器。

【问题讨论】:

  • 大多数人都在努力让他们工作,而不是失败。
  • 哈……我想验证失败是否有效,即获取一个测试用例。换行的技巧并没有帮助,因为我将其捕获并添加了\n。 (好吧,如果您计算反斜杠,它是 "\\\\n"。)
  • 好的,那我没有得到你的要求,你需要预处理器测试用例。 mcpp 有一些验证套件。

标签: c++ c c-preprocessor stringification


【解决方案1】:

stringify (#) 运算符仅在字符串常量中转义 \。事实上,\ 在字符串常量之外没有特别的意义,除了在行尾。因此,它是一个预处理标记(C 第 6.4 节,C++ 第 2.5 节)。

因此,如果我们有

#define Q(X) #X

然后

Q(\)

是一个合法调用:\ 是一个预处理令牌,它永远不会转换为令牌,因此它是有效的。但是你不能字符串化\;这会给你 "\" 这不是一个有效的字符串文字。因此,上述行为是不确定的。

这是一个更有趣的测试用例:

#define Q(A) #A
#define ESCAPE(c) Q(\c)
const char* new_line=ESCAPE(n);
const char* undefined_behaviour=ESCAPE(x);

未定义字符串化的一个不太有趣的情况是字符串化参数太长而不能成为字符串文字。 (标准建议字符串文字的最大大小至少为 65536 个字符,但不要提及宏参数的最大大小,可能更大。)

【讨论】:

  • 谢谢!应该想到的。一个未终止的字符串已经是我在原始字符串连接中测试过的东西,它会以完全相同的方式被困住:)。您更有趣的案例似乎不是预处理器中的 UB;这与写"\x" 完全一样,还是我遗漏了什么? (转义序列稍后翻译。)
  • @Potatoswatter:字符串文字包含 s 字符、转义序列和通用字符名称。 \x 以上都不是。所以"\x" 不是一个有效的字符串文字,我看到它的方式,因此预处理器如何处理ESCAPE(x)(或者,就此而言,ESCAPE(*))是未定义的。所以预处理器可以,如果它选择,用笑脸替换它们。
  • 至少在 C++ 中,“表 7 中未列出反斜杠后面的字符的转义序列是有条件的,具有实现定义的语义。”所以对于一个离散的预处理器,我认为捕获会有点限制。但你是对的,这就是语法:) 再次感谢!
  • @Potatoswatter:我同意,但我认为这不适用于\x。无论如何,对于 UB,根本没有标准,所以无论你决定什么都很酷。您甚至可以允许ESCAPE(x) "7F",尽管"\x" "7F" 可能是一个错误。 (即使那样,我也不认为这是一个需要报告的错误。)顺便说一句,你如何使用 stringify 生成原始字符串文字,更不用说将换行符放入其中?
  • Catenation 可以生成未终止的原始字符串文字。包含换行符的原始字符串文字是字符串化的有效输入。这些是不同的情况。但是相同的封装标记器用于实现这两个运算符,因此对我来说这是一个通用的代码路径。至于最后一个例子,"\x" "7F" 不允许连接到"\x7F",因为字符转换和文字连接分别发生在阶段 5 和 6。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-16
  • 1970-01-01
  • 1970-01-01
  • 2021-03-22
  • 1970-01-01
  • 1970-01-01
  • 2018-01-08
相关资源
最近更新 更多