【问题标题】:What tools can diagnose C++ portability issues due to plain char signedness?由于普通字符签名,哪些工具可以诊断 C++ 可移植性问题?
【发布时间】:2021-05-22 15:36:39
【问题描述】:

我们最近发现了一行代码,相当于

bool should_escape_control_char(char ch) {
    return (ch < 0x20);  // control chars are 0x00 through 0x1F
}

这工作如果普通char 未签名;但是如果签名了普通的char,那么这个过滤器也会意外地捕获负字符。 (最终效果是,一个朴素的 JSON 编码器将 "é" 编码为 "\u00c3\u00a9",因为对于编码器来说,它看起来像一对负字符,然后单独编码。)

IMO,这里的原罪是我们将一个普通的char 表达式与一个整数进行比较,结果取决于char 的符号。我希望编译器告诉我们:

fantasy-warning: this comparison's result may depend on the signedness of plain char
    return (ch < 0x20);  // control chars are 0x00 through 0x1F
            ^~~~~~~~~
fantasy-note: cast the operand to silence this diagnostic
    return (ch < 0x20);  // control chars are 0x00 through 0x1F
            ~~
            (signed char)(ch)

我惊讶地发现 Clang 在这种情况下没有提供警告选项;而且我在 GCC 中也没有看到任何警告选项。

  • 我只是没找对地方吗?
  • 存在哪些工具/linter/静态分析器在这种情况下会发出警告?

【问题讨论】:

  • 如果有针对这种情况的编译器警告,我相信它会发出比您对完全合法代码所能想象的更多的警告。这是一个直接的编码错误。

标签: c++ c char static-analysis tool-rec


【解决方案1】:

即使您将代码更改为,您的代码也无法移植

bool should_escape_control_char(unsigned char ch)

因为您仍在对平台上的字符编码做出假设。使用

int std::iscntrl( int ch );

取而代之,或 C 等效项,具体取决于您使用的语言。

参考https://en.cppreference.com/w/cpp/string/byte/iscntrl

(可从该站点访问 C 版本)。

【讨论】:

  • 从问题中可以清楚地看出 OP 使用 UTF-8 编码的字符。 iscntrl 在这里是错误的选择。这组值是固定的,不依赖于使用的任何语言环境。
  • @AnttiHaapala 我认为您应该提交答案。如果你愿意,请联系我。
  • 我没有解决问题的办法。您的回答也没有解决 OP 的担忧。
【解决方案2】:

我使用的静态分析器无法诊断原始示例。编写单元测试并同时使用 unsigned 和 signed char 编译它们有助于在自动化测试阶段发现此类错误。


使用无符号数时,将它们与显式无符号操作数进行比较比隐式转换有符号操作数更安全。所以,假设 char 是无符号的:

bool should_escape_control_char(char ch) {
    return ch < 0x20u;  // control chars are 0x00 through 0x1F
//                  ^
}

在这种情况下,如果假设的 char 签名错误,(至少有一些?)编译器会在 char 被签名并启用警告时发出警告:

warning: comparison of integer expressions of different signedness: 'char' and 'unsigned int' [-Wsign-compare]

与其依赖幻数,不如使用标准库中的std::iscntrl:

bool
is_control_c0(unsigned char ch) {
    return std::iscntrl(ch
        // provide locale if not using currently active
    );
}

请注意,接受单个窄字符(即代码单元)的函数无法匹配 UTF-8 中的所有控制代码点,因为 C1 控制代码被编码为两个代码单元。

【讨论】:

  • 鉴于问题中的“一个天真的 JSON 编码器将 "é" 编码为 "\u00c3\u00a9"”,我不确定“天真的 JSON 编码器”中的所有问题都可以用 @ 完全解决987654327@。我怀疑还有很多问题,因此需要更好的编译器诊断。
  • " 在技术上是编码" -> "是编码"。我不确定这有什么“技术上的”。
  • @Bathsheba “技术上”之所以存在,是因为有 许多 个编码适用于该假设,以及 许多 个用例不需要关心不适用的编码。
  • 我要求的是对原始(错误)代码发出警告的工具,而不是在代码编写不同时会发出警告的工具。如果我有一台时光机可以回去告诉程序员以不同的方式编写它,我会告诉他们首先添加演员表。
  • @Quuxplusone 就像我在回答中所说:单元测试。我只是展示了一种在对符号进行假设时更安全的编写程序的方法。
猜你喜欢
  • 2015-08-30
  • 1970-01-01
  • 1970-01-01
  • 2010-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-14
相关资源
最近更新 更多