2017-12-02 301 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; 
}