是否有GCC警告可以检测有符号类型的位移操作?

13
如果我对C++ ISO规范(5.8.2和5.8.3节)的阅读正确的话,负有符号类型的右移是实现相关的,左移则是未定义行为。
因此,我想在我们使用g++ 4.8.2编译的遗留源代码中找到有符号类型的移位操作。
不幸的是,在手册中我找不到这样的选项。例如,我可以使用"g++ -Wall -Wextra -pedantic"编译此代码而没有警告:
int si    = -1;
int left  = si << 1; // -2 (multiplication by 2, sign is preserved)
int right = si >> 1; // -1 (no change, only 1s)

有人能告诉我是否存在这样的警告,如果不存在,为什么gcc不关心它呢?

2个回答

6
据我所知,gcc不提供这样的选项。正如你引用的标准所述:
N3690 - §5.8.3
“E1 >> E2”的值是将E1向右移动E2位。如果E1具有无符号类型或者E1具有带非负值的有符号类型,则结果的值为E1除以2E2的商的整数部分。如果E1具有带负值的有符号类型,则结果的值是实现定义的。”
这意味着要执行以下操作:
int si    = -1;
int right = si >> 1;

可能会得到-1的结果,也可能不会。这是“实现定义”的。这意味着编译器不强制发出警告,就像“其他编译器可能以另一种方式执行此操作”一样。
这种选择的一些原因如下。
最初的K&R文章说:
“将无符号数量向右移位,用0填充空置位。将有符号数量向右移位将使用符号位(算术移位)填充某些机器(例如PDP-11),并在其他机器上使用0位(逻辑移位)。 ”
这意味着该操作依赖于体系结构。背后的原因是某些架构在执行其中一种操作时非常快,但不能同时执行两种操作。
这个原因再加上符号扩展移位的实用性较小,使得标准选择将其留作“实现定义”。通过“符号扩展移位的实用性”,我指的是对于负的有符号整数进行算术右移操作与对应的正整数不同(因为在右侧丢失1会使负数变小,即模数变大)。
+63 >> 1 = +31 (integral part of quotient E1/2E2)
00111111 >> 1 = 00011111
-63 >> 1 = -32 
11000001 >> 1 = 11100000

进一步阅读的参考资料:

https://dev59.com/DnI-5IYBdhLWcg3wbXxN#1857965

http://www.ccsinfo.com/forum/viewtopic.php?t=45711

https://isocpp.org/std/the-standard


编辑:如果以上内容未解决问题(即代码有效,编译器为何要发出警告?),我提供第二种解决方案:AST匹配器

如此描述:http://eli.thegreenplace.net/2014/07/29/ast-matchers-and-clang-refactoring-tools/ 您可以编写一些代码,快速识别程序中所有使用有符号整数进行右移的位置。

把它当作“编写我的小型单任务静态分析检查器”。


编辑2:您还可以尝试其他静态分析工具,clang有一个-fsanitize=shift选项,可能对您有用。据我所知,对于gcc他们正在实现未定义行为的消毒剂,这可能有助于诊断这些错误。我没有一直在关注这个故事,但我想您也可以试试。


1
厉害了,你对标准很了解,但你也试图回答上面的问题吗? - Tilman Vogel
3
这个回答中提供了许多关于右移的有用信息,但我没有看到任何与题主问题相关的内容。它可以成为回答另一个问题的好答案,但仅此而已。 - user743382
2
@MarcoA。编译器经常会根据设计发出有关完全有效的代码的警告,因此它完全有效并不解决编译器为什么不发出警告的问题。这确实回答了编译器为什么不给出错误消息的问题,但这不是 OP 提出的问题。 - user743382
@MarcoA。但是你编辑中的链接看起来是一个好的、有用的方法。虽然它并不完全符合原帖的要求,但它确实在帮助原帖作者实现他所想要的东西上做出了很大的贡献,因此我已经取消了我的踩票。 - user743382
1
我同意他们可能会发出警告,特别是对于旧代码,但我认为除非标准明确规定,否则这取决于编译器实现。我不知道有任何选项可以做到这一点(我编辑了问题以指定它),并提供了为什么标准没有强制执行的原因。我同意gcc 可以,但它并没有被强制,所以..."为什么应该呢?"似乎也是一个有效的回答。 - Marco A.
显示剩余6条评论

2

这里是GCC文档链接

答案似乎是否定的,没有你想要的任何警告选项。

我不知道为什么,但我猜可能是因为有很多代码使用了这个操作,这样会产生太多噪音。虽然C语言没有定义算术移位,但现在大多数CPU都采用相同的方式执行,因此大多数人认为它是被定义成这种方式的。


警告不必默认启用。但检测可移植性问题会很有用。对于有符号类型的补码也是如此。 - vinc17
-Wshift-overflow=2 - 0andriy

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