man*_*dle 13 c++ gcc gcc-warning gcc4.8
如果我正确地阅读了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)
Run Code Online (Sandbox Code Playgroud)
任何人都可以告诉我是否有这样的警告,如果没有,为什么gcc不关心它?
AFAIK gcc不提供这样的选择.正如你所引用的那样,标准说:
N3690 - §5.8.3
E1 >> E2的值是E1右移E2位位置.如果E1具有无符号类型或者E1具有有符号类型和非负值,则结果的值是E1/2E2的商的整数部分.如果E1具有带符号类型和负值,则结果值是实现定义的.
这意味着要做
int si = -1;
int right = si >> 1;
Run Code Online (Sandbox Code Playgroud)
结果可能会或可能不会产生-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
Run Code Online (Sandbox Code Playgroud)
参考资料进一步阅读:
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他们实施了一个未定义的行为消毒剂,可以帮助诊断这些错误.我没有关注这个故事,但我猜你也可以尝试一下.