PVS-Studio是否了解Unicode字符?

时间:2017-12-02 17:00:19

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

此代码在w / 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不知道什么? ;)

1 个答案:

答案 0 :(得分:2)

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

我认为这个代码是在char为signed char类型时编译的。让我们看看它带来了什么后果。

我们只看第一种情况:

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

如果值变量&#39; c&#39;小于或等于01111111,条件将始终为假,因为在移位期间,您可以获得的最大值为011。

这意味着我们只对变量中最高位&#39; c&#39;等于1.由于此变量是signed char类型,因此最高位表示变量存储负值。在转换之前,signed char变为signed int并且值继续为负。

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

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

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

PVS-Studio认为最高位用1填充。它有充分的权利这么想,因为有必要选择任何实现。因此,如果变量&#39; c&#39;中的最高位,则表达式((c)>> 5)将具有负值。最初等于1.负数不能等于TWO_SYMBOLS_MASK。

事实证明,从PVS-Studio的角度来看,条件总是错误的,它会正确发出警告V547。

在实践中,编译器可能表现不同:最高位将填充为0,然后一切都将正常工作。

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

代码可能修复如下:

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