PVS-Studio 知道 Unicode 字符吗?
Does PVS-Studio know about Unicode chars?
此代码在包含 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 不知道什么? ;)
PVS-Studio 分析器知道有signed 和unsigned char 类型。是否使用 signed/unsigned 取决于编译密钥,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的情况感兴趣。由于这个变量是signed char类型,那么最高位表示变量存储负数价值。移位前,signed char变成了signed int,值继续为负。
现在让我们看看标准对负数右移的说法:
E1>>E2的值是E1右移E2位的位置。如果 E1 是无符号类型或者如果 E1 是有符号类型和非负值,则结果的值是 E1/2^E2 的商的整数部分。如果 E1 具有带符号类型和负值,则结果值是实现定义的。
因此,负数向左移动是实现定义的。这意味着最高位用空值或 1 填充。两者都是正确的。
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;
}
此代码在包含 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 不知道什么? ;)
PVS-Studio 分析器知道有signed 和unsigned char 类型。是否使用 signed/unsigned 取决于编译密钥,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的情况感兴趣。由于这个变量是signed char类型,那么最高位表示变量存储负数价值。移位前,signed char变成了signed int,值继续为负。
现在让我们看看标准对负数右移的说法:
E1>>E2的值是E1右移E2位的位置。如果 E1 是无符号类型或者如果 E1 是有符号类型和非负值,则结果的值是 E1/2^E2 的商的整数部分。如果 E1 具有带符号类型和负值,则结果值是实现定义的。
因此,负数向左移动是实现定义的。这意味着最高位用空值或 1 填充。两者都是正确的。
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;
}