Вычитание целых чисел без знака в C и получение предупреждений -Wconversion - PullRequest
0 голосов
/ 24 августа 2018

Учтите:

uint16_t x;
uint16_t y;

y = 0;
x = y - 1;

X будет каким-то безумным числом. См. Другие статьи SO для «почему» и чтобы узнать о комплименте от 2 .

(Я не спрашиваю, почему это так. Я знаю почему. Я спрашиваю кое-что ещеЧитайте дальше.)

Если вы включите флаг gcc -Wconversion, этот совершенно правильный код C, приведенный выше, выдаст conversion to ‘uint16_t {aka short unsigned int}’ from ‘int’ may alter its value Это компилятор, пытающийся помочь нам не испортить.И я склонен программировать с включенными такими предупреждениями, потому что они помогают мне избежать путаницы.

Однако существуют реалистичные случаи, когда мы можем использовать unsigned int в качестве счетчика и захотим вычесть 1 и НЕ получить это предупреждение,Кроме того, мы хотим использовать его в качестве счетчика, уменьшить его до нуля, а затем просто остановить.Чтобы сделать это в C, мы должны поставить какую-то проверку.Тем не менее, этот вид контрольного кода будет нарушать предупреждение gcc.

Как и код выше.

Я хочу свой торт и хочу его съесть тоже.

Какое элегантное решение для этого?

Ответы [ 3 ]

0 голосов
/ 24 августа 2018

В этом коде

x = y - 1;

Аргумент y подчиняется «целочисленное продвижение по умолчанию» в соответствии со стандартом C.

Это означает, что y - 1 является значением int, а затем этот результат int возвращается обратно к uint16_t через усечение. Это то, на что жалуется GCC.

Способ устранения проблемы - явное приведение результата к uint16_t:

x = ( uint16_t ) ( y - 1 );

Полное объяснение можно найти в 6.3.1.1 Булевы, символы и целые числа :

Каждый целочисленный тип имеет ранг целочисленного преобразования, определенный следующим образом:

  • Никакие два целых типа со знаком не должны иметь одинаковый ранг, даже если они имеют одинаковое представление.
  • Ранг целочисленного типа со знаком должен быть больше ранга целочисленного типа со знаком с меньшей точностью.
  • Ранг long long int должен быть больше ранга long int, который должен быть больше, чем ранг int, который должен быть больше, чем ранг короткого int, который должен быть больше, чем ранг подписанного символа.
  • Ранг любого целочисленного типа без знака должен равняться рангу соответствующего целочисленного типа со знаком, если таковой имеется.
  • Ранг любого стандартного целочисленного типа должен быть больше, чем ранг любого расширенного целочисленного типа с такой же шириной.
  • Ранг чар равняется званию подписанного и без знака.
  • Ранг _Bool должен быть меньше, чем ранг всех других стандартных целочисленных типов.
  • Ранг любого перечислимого типа должен равняться рангу совместимого целочисленного типа (см. 6.7.2.2).
  • Ранг любого расширенного целочисленного типа со знаком относительно другого расширенного целочисленного типа со знаком с той же точностью равен определяется реализацией, но все еще подчиняется другим правилам для определение ранга целочисленного преобразования.
  • Для всех целочисленных типов T1, T2 и T3, если T1 имеет больший ранг, чем T2, и T2 имеет больший ранг, чем T3, тогда T1 имеет больший ранг, чем T3.

Следующее может использоваться в выражении везде, где int или без знака int может использоваться:

  • Объект или выражение с целочисленным типом (кроме int или unsigned int), чей ранг целочисленного преобразования меньше или равен звание int и без знака int.
  • Битовое поле типа _Bool, int, sign int или unsigned int.

Если int может представлять все значения исходного типа (как ограничено по ширине для битового поля) значение преобразуется в int; в противном случае он конвертируется в беззнаковое целое. Это называется целочисленные акции. Все остальные типы не меняются целым числом акции.

Целочисленные рекламные акции сохраняют значение, включая знак. Как обсуждалось ранее, считается ли «обычный» символ подписанным реализации.

0 голосов
/ 24 августа 2018

Выражение:

x = y - 1;
На

влияет целочисленное повышение , как подробно описано в разделе 6.3.1p2 C стандарта :

Следующее может использоваться в выражении везде, где int или без знака int может использоваться:

  • Объект или выражение с целочисленным типом (кроме int или unsigned int), чей ранг целочисленного преобразования меньше или равен звание int и без знака int.
  • Битовое поле типа _Bool, int, sign int или unsigned int.

Если int может представлять все значения исходного типа (как ограничено ширина, для битового поля), значение преобразуется в int; в противном случае он конвертируется в беззнаковое целое. Это называется целочисленные акции. Все остальные типы не меняются целым числом акции.

Поскольку y имеет тип uint16_t, который имеет более низкий ранг, чем int, он повышается до int. Затем это вычитается константой 1, которая также имеет тип int, поэтому результат y - 1 имеет тип int. Это значение затем присваивается uint16_t, который имеет более низкий ранг, чем int, поэтому вы получаете предупреждение.

Вам нужно привести всю правую сторону к uint16_t, чтобы избежать предупреждения:

x = (uint16_t)(y - 1);

Обратите внимание, что недостаточно привести один или оба операнда оператора -, поскольку правила целочисленного продвижения все равно вступят в силу, если в выражении будет использовано значение ранга, меньшее int.

0 голосов
/ 24 августа 2018

Явно приведен y-1 к uint16_t. GCC жалуется не на вычитание, а на то, что вы можете потерять информацию после того, как результат выражения будет присвоен (теперь меньшему) типу. (Интегральные операнды автоматически переводятся в int при использовании в выражениях, когда типы меньше и могут помещаться в int со знаком без потери точности.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...