如果我正确阅读C ++ ISO规范(第5.8.2和5.8.3节),负签名类型的右移是特定于实现的,左移未定义行为。
因此,我想在我们使用g ++ 4.8.2编译的遗留源代码中找到对签名类型的移位操作。
不幸的是,我在manual找不到这样的选项。 我可以使用“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不关心呢?
答案 0 :(得分:6)
AFAIK gcc没有提供这样的选择。正如你所引用的那样,标准说:
N3690 - §5.8.3
E1的值&gt;&gt; E2是E1右移E2位位置。如果E1有 无符号类型或E1具有有符号类型和非负值, 结果的值是商的整数部分 E1 / 2E2。如果E1具有带符号类型和负值,则生成 值是实现定义的。
这意味着要做
int si = -1;
int right = si >> 1;
结果可能会或可能不会产生-1。它是实现定义。这意味着编译器不会强制发出警告,如“其他编译器可能会以另一种方式执行此操作”。
这种选择的一些原因如下。
最初的K&amp; 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://stackoverflow.com/a/1857965/1938163
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选项,可以为您服务。 AFAIK也为gcc他们实施了一个未定义的行为清理程序,可以帮助诊断这些错误。我没有关注这个故事,但我想你也可以尝试一下。
答案 1 :(得分:2)
GCC文档为here。
答案似乎是“不”,没有任何警告选项可以用于你想要的东西。
我不知道为什么,但我猜这里有很多代码使用它会太吵了。 C可能没有定义算术移位,但是现在几乎所有的CPU都以相同的方式执行,因此大多数人都认为它是以这种方式定义的。