【问题标题】:Does PVS-Studio know about Unicode chars?PVS-Studio 知道 Unicode 字符吗?
【发布时间】:2018-05-16 13:22:33
【问题描述】:

此代码在带有 return 的行产生中等警告:

// Checks if the symbol defines two-symbols Unicode sequence
bool doubleSymbol(const char c) {
    static const char TWO_SYMBOLS_MASK = 0b110;
    return (c >> 5) == TWO_SYMBOLS_MASK;
}

// Checks if the symbol defines three-symbols Unicode sequence
bool tripleSymbol(const char c) {
    static const char THREE_SYMBOLS_MASK = 0b1110;
    return (c >> 4) == THREE_SYMBOLS_MASK;
}

// Checks if the symbol defines four-symbols Unicode sequence
bool quadrupleSymbol(const char c) {
    static const char FOUR_SYMBOLS_MASK = 0b11110;
    return (c >> 3) == FOUR_SYMBOLS_MASK;
}

PVS 表示表达式总是错误的 (V547),但实际上不是:char 可能是读取到 std::string 的 Unicode 符号的一部分! 以下是符号的 Unicode 表示:
1 byte - 0xxx'xxxx - 7 bits
2 bytes - 110x'xxxx 10xx'xxxx - 11 bits
3 bytes - 1110'xxxx 10xx'xxxx 10xx'xxxx - 16 bits
4 bytes - 1111'0xxx 10xx'xxxx 10xx'xxxx 10xx'xxxx - 21 bits

以下代码计算 Unicode 文本中的符号数:

size_t symbolCount = 0;

std::string s;
while (getline(std::cin, s)) {
    for (size_t i = 0; i < s.size(); ++i) {
        const char c = s[i];
        ++symbolCount;
        if (doubleSymbol(c)) {
            i += 1;
        } else if (tripleSymbol(c)) {
            i += 2;
        } else if (quadrupleSymbol(c)) {
            i += 3;
        }
    }
}

std::cout << symbolCount << "\n";

对于Hello! 输入,输出是6,对于Привет, мир!,输出是12——没错!

我错了还是 PVS 不知道什么? ;)

【问题讨论】:

  • 这可能是signed unsigned char 转换问题。
  • @user0042 所以我不明白。如果有问题,为什么它会起作用?..
  • 这是一个潜在的问题。您的char 是签名还是未签名,因为移位运算符会给出不同的结果。 PVS 是否知道char 是否已签名?
  • 'static const char TWO_SYMBOLS_MASK = 0b110;'怎么样不是彻头彻尾的错误?您指定常量为八进制(前导 0,后跟 x 或 X),然后使用十六进制数字。
  • @SornelHaetir,这是一个二进制文字,而不是八进制。

标签: c++ unicode windows-10 visual-studio-2017 pvs-studio


【解决方案1】:

PVS-Studio 分析器知道有带符号和无符号字符类型。是否使用签名/未签名取决于编译密钥,PVS-Studio 分析器会考虑这些密钥。

我认为这段代码是编译的,当 char 是有符号字符类型时。让我们看看它会带来什么后果。

我们只看第一种情况:

bool doubleSymbol(const char c) {
    static const char TWO_SYMBOLS_MASK = 0b110;
    return (c >> 5) == TWO_SYMBOLS_MASK;
}

如果值变量 'c' 小于等于 01111111,则条件始终为 false,因为在移位期间,您可以获得的最大值为 011。

这意味着我们只对变量“c”的最高位等于 1 的情况感兴趣。由于该变量是有符号字符类型,因此最高位表示该变量存储负值。在移位之前,signed char 变成了有符号的 int,并且值继续为负数。

现在让我们看看标准对负数右移的说法:

E1 >> E2 的值是 E1 右移 E2 位的位置。如果 E1 具有无符号类型或 E1 具有带符号类型和非负值,则结果的值是 E1/2^E2 的商的整数部分。如果 E1 具有带符号类型和负值,则结果值是实现定义的。

因此,负数左移是实现定义的。这意味着最高位用空值或一填充。两者都是正确的。

PVS-Studio 认为最高位用 1 填充。它完全有这样的想法,因为有必要选择任何实现。因此,如果变量“c”的最高位最初等于 1,则表达式 ((c) >> 5) 将具有负值。负数不能等于 TWO_SYMBOLS_MASK。

事实证明,从 PVS-Studio 的角度来看,条件始终为 false,并且正确发出警告 V547。

实际上,编译器的行为可能会有所不同:最高位将用 0 填充,然后一切都会正常工作。

在任何情况下,都需要修复代码,因为它涉及编译器的实现定义的行为。

代码可能固定如下:

bool doubleSymbol(const unsigned char c) {
    static const char TWO_SYMBOLS_MASK = 0b110;
    return (c >> 5) == TWO_SYMBOLS_MASK;
}

【讨论】:

    猜你喜欢
    • 2018-08-18
    • 2021-12-06
    • 1970-01-01
    • 2017-10-05
    • 1970-01-01
    • 2019-06-03
    • 2019-03-21
    • 2018-06-08
    • 2019-01-07
    相关资源
    最近更新 更多