为什么这种转变的结果会被视为未定义?

0xC*_*22L 10 c clang-static-analyzer language-lawyer

我正在使用C90和C99的混合物(不能完全使用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;
}
Run Code Online (Sandbox Code Playgroud)

当然,传递给的值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.
Run Code Online (Sandbox Code Playgroud)

现在逐一进行:

// 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
Run Code Online (Sandbox Code Playgroud)

所以,好吧它将所有有意义的位从整数中推出,并且根据我对以下内容的理解(从这里开始),这应该只给我零:

6.5.7按位移位运算符

...

4

结果E1 << E2E1左移位E2位置; 腾出的位用零填充.如果E1具有无符号类型,则结果的值E1 × 2^E2将比结果类型中可表示的最大值模数减1.如果E1具有有符号类型和非负值,并且E1 × 2^E2在结果类型中可表示,那么这就是结果值; 否则,行为未定义.

根据我的阅读方式,如果涉及有符号值,则只能发生未定义的结果.但是,我注意到所有值都是无符号的,甚至在文字上都是明确的.

我错了还是Clang静态分析仪过于热心?


该代码的原始版本来自C++中的Jonathan Bennetts JB01实现(版本1.40a).

Yu *_*Hao 7

在C99标准中,就在您引用的部分之前:

3

对每个操作数执行整数提升.结果的类型是提升的左操作数的类型.如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义.

unsigned int在今天的大多数机器中有32位,这使得左移33,未定义的行为.