我同时使用C90和C99(由于某些原因,我最好不要讨论这些原因,因为它们会让我的血压升高并危及阻止我们将代码库移动到当前千年的人的生命)。尽管如此,我还是会引用C99标准。
我有类似以下紧凑形式的代码(test.c
):
#include <stdio.h>
unsigned int foo(unsigned int n)
{
unsigned int x, y;
n = n - 264;
x = (n >> 2) + 1;
y = 1U << (x + 2U);
return y;
}
int main(void)
{
printf("%u\n", foo(384));
return 0;
}
当然,传递给 foo()
的值可以比此处给出的值更大。但是384是最低值,将触发Clang静态分析器(从发布标签编译的3.4版本)发出警告。$ clang -cc1 -triple x86_64-unknown-linux-gnu -analyze -analyzer-checker=core -internal-isystem /usr/local/include -internal-isystem $HOME/bin/LLVM/bin/../lib/clang/3.4/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -x c test.c
test.c:8:9: warning: The result of the '<<' expression is undefined
y = 1U << (x + 2U);
~~~^~~~~~~~~~~
1 warning generated.
现在逐行查看代码:
// n == 384
n = n - 264; // n := 384 - 264
// n == 120
x = (n >> 2) + 1; // x := (120 div 4) + 1
// x == 31
y = 1U << (x + 2U); // y := 1 << 33
所以,它将整数中的所有有意义的位都推出来了,根据我对以下内容的理解(来自 这里),这应该只会给我简单的零:
从我的阅读中,如果涉及到有符号值,那么未定义的结果只可能发生一次。但是,我确保所有值都是无符号的,甚至在文字上也明确说明了。6.5.7 位移操作符
...
4
E1 << E2
的结果是E1
左移E2
个位位置; 腾空的位被填充为0。如果E1
具有无符号类型,则结果的值为E1 × 2^E2
,取模于比结果类型中可表示的最大值多一个。如果E1
具有带符号类型且非负值,并且E1 × 2^E2
可在结果类型中表示,则该值即为结果;否则,行为未定义。
我错了还是Clang静态分析器过于热衷?
此代码的原始版本来自Jonathan Bennetts JB01 C++实现(版本1.40a)。