"使用带符号整数操作数进行二进制位运算" - 在使用无符号短整型时

66
在下面的C代码片段中,检查一个16位序列的前两位是否被设置:
bool is_pointer(unsigned short int sequence) {
  return (sequence >> 14) == 3;
}

CLion的Clang-Tidy给我一个"使用带符号整数操作数进行二进制按位运算符操作"的警告,但我不明白为什么。是unsigned short不够无符号吗?


10
听起来像是一个漏洞。 - Oliver Charlesworth
6
正在JetBrains CLion中进行修复。2018年有关于clang-tidy bug 36961的讨论,但尚未修复。 - Roland Illig
2
我联系了Perforce支持团队,询问了这个问题的澄清。让我们看看他们会回复什么。(Perforce是clang-tidy实现的标准的维护者,而clang-tidy又被用于CLion中。) - Roland Illig
有时候当你收到警告时,说明你一开始就不应该做那件事情。 - JosephDoggie
@JosephDoggie — 同意忽视警告是不好的,通常情境很重要,而且经常被忽略。在这里,看起来_"CLion 的 Clang-Tidy 给我..."_提供了相关的情境。但是我没有看到 OP 的 意图 如何帮助理解为什么他们试图对一个无符号整数进行位移而收到警告。也许这可以帮助找到替代解决方案。 - ad absurdum
显示剩余3条评论
3个回答

88

此警告的代码检查位运算符的任一操作数是否为有符号。引起警告的不是sequence,而是14,您可以通过在末尾添加u使14变为无符号来缓解问题。

(sequence >> 14u)

这个警告很糟糕。正如Roland's answer所描述的那样,CLion正在修复这个问题。

7
哇……真的是哇……我在想为什么它没有抱怨将sequence 提升为signed int - Antti Haapala -- Слава Україні
3
@AnttiHaapala 这是一个相当奇怪的检查,我同意。 - Ryan Haining
1
我接受了这个答案,因为它消除了警告,而另一个没有 :) - SakoDaemon
3
让“警告消失”并不总是好的。在某些情况下(比如这种情况),警告是错误的,而不是你的代码。不要盲目相信警告。相反,尝试理解它们,然后决定谁是错的。 - Roland Illig

33
clang-tidy中有一个名为hicpp-signed-bitwise的检查规则,它遵循HIC++标准的措辞。该标准可以免费获取,其中规定:
5.6.1 不要对带符号的操作数使用按位运算符。
使用带符号操作数的按位运算符在某些情况下会导致未定义或实现定义的行为。因此,按位运算符只应与无符号整数类型的操作数一起使用。
HIC++编码标准的作者误解了C和C++标准的意图,并错误地将重点放在操作数的类型而不是上。
clang-tidy中的这个检查规则严格实现了这个措辞,以符合该标准。该检查规则并不是通用的,它的唯一目的是帮助那些必须符合HIC++标准的可怜程序员遵守这个愚蠢的规则。
关键问题在于,根据定义,在没有任何后缀的整数字面量中,类型为int,而该类型被定义为带符号类型。HIC++标准现在错误地得出结论,即正整数字面量可能是负数,因此可能会引发未定义行为

为了比较,C11标准的规定如下:

6.5.7 位移运算符

如果右操作数的值为负数或者大于等于提升后的左操作数的宽度,则行为未定义。

这个措辞是经过精心选择的,强调右操作数的很重要,而不是类型。它也覆盖了一个过大的值的情况,而HIC++标准只是忘记了这种情况。因此,在HIC ++中说1u << 1000u是可以的,而1 << 3则不行。

最好的策略是明确禁用此单个检查。有几个CLion的错误报告提到了这一点,并且正在修复。


更新2019-12-16:我问Perforce这个精确措辞背后的动机是什么以及这个措辞是否是有意的。以下是他们的回答:

我们参与创建HIC ++标准的C ++团队已经查看了您提到的Stack Overflow问题。

简而言之,在HIC++规则中引用对象类型而不是值是一种有意的选择,以便更轻松地自动检查代码。对象的类型始终已知,而值则不是。

  • HIC++规则总体上旨在“可决定性”。 对类型进行强制执行,以确保始终可以进行可决定性检查,即直接在使用运算符的地方或将有符号类型转换为无符号类型的地方。
  • 基本原理明确提到了“可能”的未定义行为,因此合理的实现可以排除以下情况:
    • 常量,除非肯定存在问题
    • 将无符号类型提升为有符号类型。
  • 因此,对于CLion而言,最佳操作是将检查限制在升级之前的非常量类型。

1
潜在的问题是当负值向右移位时 - CPU 是否会在前面添加0(之前没有任何东西)或1(保持负性)? - Pnemonic
1
@Pnemonic 是的,这是另一个未定义行为的领域。但这与HICPP检查无关,它涉及到允许负值类型和可能为负的值之间的混淆。 - Roland Illig
3
要禁用CLion中的规则,请转到文件>设置/首选项>编辑器>检查,在右侧窗格中选择Clang-Tidy。 在选项下,写入一个逗号和“-hicpp-signed-bitwise”并按下OK。 - mic
1
@mic 感谢你提供的解决方法。虽然这不是一个合适的解决方案。如果我在我的个人IDE副本中禁用规则,这并不能防止其他人也感到困惑。因此,唯一明智的做法是告诉这个错误检查的分发者(在这种情况下是JetBrains,以及更上游的Clang-Tidy)来修复这个问题。 - Roland Illig
与完全禁用检查器相比,如果您不想将“U”添加到您所谓的无符号字面量中,更好的方法是仅禁用检查器的该情况,在您的配置中使用- key: hicpp-signed-bitwise.IgnorePositiveIntegerLiterals - undefined

10

我认为这里的整数提升导致了警告。小于int的操作数被扩展为有符号的算术表达式所需的整数。因此,您的代码实际上是 return ( (int)sequence >> 14)==3;,这导致了警告。尝试使用 return ( (unsigned)sequence >> 14)==3;return (sequence & 0xC000)==0xC000;


4
当我阅读这个问题时,我的想法也是如此......但在提交自己的答案之前,(目前被接受的)另一个答案出现了,这让我相信Oliver是正确的...... [C11/6.3.1.1p2](https://port70.net/~nsz/c/c11/n1570.html#6.3.1.1p2)是你的引用,顺便说一句。 - autistic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接