Почему исход этого сдвига влево будет считаться undefined?

Я работаю с сочетанием 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;
}

Разумеется, значение, переданное в foo(), может быть больше значения, указанного здесь. Тем не менее 384 - это самое низкое значение, которое вызовет статический анализатор Clang (3.4, скомпилированный из тега release), чтобы направить предупреждение:

$ 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 битовых позиций; освобожденные биты заполняются нулями. Если E1 имеет без знака тип, значение результата E1 × 2^E2, приведено по модулю еще один чем максимальное значение, представляемое в типе результата. Если E1 имеет подписанный тип и неотрицательное значение, а E1 × 2^E2 представим в тип результата, то это результирующее значение; в противном случае поведение undefined.

Из того, как я это прочитал, результат undefined может произойти только когда-либо, если задействованы подписанные значения. Тем не менее, я позаботился о том, чтобы все значения были неподписанными, даже сделав их явными в литералах.

Я ошибаюсь или статический анализатор Clang чрезмерно ревностен?


Оригинальное воплощение этого кода происходит от реализации Джонатана Беннетта JB01 (версия 1.40a) в С++.

Ответ 1

В стандарте C99, прямо перед цитируемой частью:

3

Целочисленные рекламные акции выполняются для каждого из операндов. Тип результата это продвинутый левый операнд. Если значение правого операнда отрицательное или больше или равно ширине продвинутого левого операнда, поведение undefined.

unsigned int в большинстве машин сегодня имеет 32 бита, что делает поведение сдвига влево 33, undefined.

Ответ 2

В этом же пункте также говорится, что перед частью, указанной вами, в пункте 6.5.7.3:

Если значение правого операнда отрицательное или больше или равно ширине продвинутого левого операнда, поведение undefined.

Таким образом, clang выполняет прекрасную работу, так как поведение действительно undefined после того, как вы сдвинете больше бит, чем может удерживаться продвинутый левый операнд.