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

Имеет ли выражение сдвига 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 может выдавать это предупреждение?
4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

Стандарты C99 - C17 говорят:

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

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

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