Имеет ли выражение сдвига C беззнаковый тип? Зачем Сплинту предупреждать о смещении вправо?

Для следующей программы:

int main(void)
{
    int value = 2;
    int result = value >> 1U;
    return result;
}

...Шина 3.1.2 выдает предупреждение:

splint_test.c: (in function main)
splint_test.c:4:18: Variable result initialized to type unsigned int, expects
                       int: value >> 1U
  To ignore signs in type comparisons use +ignoresigns

Похоже, что Splint утверждает, что выражение, в котором целое число со знаком сдвинуто вправо, имеет тип целого числа без знака. Однако все, что я могу найти в стандарте ANSI C90, это:

Результатом E1 >> E2 является E1 сдвинутых вправо E2 битовых позиций. Если E1 имеет беззнаковый тип или если E1 имеет знаковый тип и неотрицательное значение, значение результата является целой частью частного E1, деленного на количество, 2, возведенное в степень E2.

Основной целью для этого кода является встроенная система с компилятором, в основном C90. Однако я заинтересован в написании кода, соответствующего стандартам. Я тестировал GCC и Clang в режиме C99, так что restrict работает.

Мои вопросы:

  1. Делает ли стандарт C какие-либо заявления о типе результата битового сдвига?
  2. Компиляторы?
  3. Если нет, то почему Splint может выдавать это предупреждение?

person detly    schedule 14.04.2019    source источник
comment
Вы смотрели в исходном коде Splint, обрабатывает ли он >> так же, как и другие бинарные операторы? Это было бы ошибкой в ​​​​Splint, и, поскольку текущая версия, похоже, такая же, как и в 2007 году, я не думаю, что с тех пор что-то изменилось.   -  person Roland Illig    schedule 14.04.2019
comment
github.com/ravenexp/splint/blob/ ; фактическая ошибка находится в строке 5775, которая принимает в качестве результата более широкий тип, а не просто принимает тип левого операнда.   -  person Roland Illig    schedule 14.04.2019
comment
clang — гораздо лучший вариант, чем splint, для статического анализа в наши дни, особенно с учетом того, что с момента последнего обновления splint прошло более десяти лет.   -  person Shawn    schedule 14.04.2019
comment
@Shawn Clang не выполняет строгую проверку typedef. Ничто, кроме Splint, не делает (насколько я знаю).   -  person detly    schedule 14.04.2019
comment
@Shawn Хотя спасибо, что напомнили мне о сканировании-сборке, теперь я добавлю его в свою конфигурацию CI!   -  person detly    schedule 14.04.2019


Ответы (3)


Это ошибка в Сплинте. Splint ошибочно предполагает, что тип e1 << e2 равен ctype_wider(te1, te2). Правильный тип будет просто te1.

код с ошибками начинается здесь с использованием того же кода путь для побитовых операторов, таких как &, | и ^, а также для операторов << и >>.

настоящая ошибка находится в конце этого кода , который предполагает, что для всех этих побитовых бинарных операторов возвращаемый тип — ctype_wider(te1, te2).

Я открыл ошибку в системе отслеживания ошибок Splint GitHub, ссылаясь на этот вопрос.


Обновление, февраль 2021 г.:

в отчете NetBSD говорится, что в традиционном C операторы побитового сдвига применяли к своим операндам обычные арифметические преобразования:

    /* Make sure both operands are of the same type */
    if (mp->m_balance_operands || (tflag && (op == SHL || op == SHR)))
        balance(op, &ln, &rn);

Объяснение кода:

Это было изменение в C90. Таким образом, код Splint мог быть правильным для традиционного C, возможно, он просто не был обновлен для C90 или C99.

person Roland Illig    schedule 14.04.2019

Нет. Стандарт говорит, что тип битового сдвига — это тип левого операнда, продвигаемый: 6.5.7p3

... Тип результата - это повышенный левый операнд. ...

Ваш инструмент должен запутаться, выводя тип с обычным арифметическим преобразованием, которое применимо к большинству бинарных операторов, но не к << и >>.

Вы также можете проверить тип int, вставив утверждение типа на основе _Generic и просмотрев что компиляторы принимают это:

int main(void)
{
    int value = 2;
    int result = _Generic(value >> 1U, int: value>>1U); //compiles, the type is int
    return result;
}
person PSkocik    schedule 14.04.2019

В стандартах от C99 до C17 говорится:

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

Поскольку value является int, он не требует повышения, а тип «повышенного левого операнда» — int, а тип результата << такой же.

C89/C90 говорит то же самое, за исключением того, что слово «целое» заменено на «интегральное».

person hobbs    schedule 14.04.2019