2017-12-02 302 views
1

该代码在生产线中的警告瓦特/ returnPVS-Studio是否了解Unicode字符?

// 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可以是Unicode符号的一部分被读取到std::string! 下面是符号的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知道些什么? ;)

+2

这可能是一个''signed'签名char'转换的问题。 – user0042

+0

@ user0042所以我不明白。如果有问题,它为什么会起作用? – SerVB

+0

这是一个潜在的问题。你的'char'有符号还是无符号,因为移位运算符会给出不同的结果。并且PVS是否知道'char'是否被签名? –

回答

2

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

我认为这个代码是编译的,当char是signed char类型的时候。让我们看看它带来的后果。

让我们来看看只在第一种情况:

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

如果变量的值“C”小于或等于01111111,条件永远是假的,因为在换挡期间的最大值即可得到是011.

这意味着我们只关心变量'c'中最高位等于1的情况。由于这个变量是带符号字符类型,所以最高位表示变量存储一个负值。在转换之前,有符号的char变成了一个有符号的int,并且该值继续为负。

现在让我们来看看有什么标准说,关于负数右移:

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

因此,向左移动一个负数是实现定义的。这意味着最高位填充了零位或零位。两者都是正确的。

PVS-Studio认为最高位填充了1。它有充分的权利去思考,因为有必要选择任何实现。因此,如果变量'c'中的最高位最初等于1,则表达式((c)>> 5)将具有负值。负数不能等于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; 
}