Передача от "int" до "unsigned short" после применения побитового оператора "~"

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

uint16 var1 = 1U;
uint16 var2 = ~var1;

Я проверяю правила MISRA C 2004, и я нахожу правило 10.5:

Если побитовые операторы ~ и < применяются к операнду od base type unsigned char или unsigned short, результат должен быть немедленно перенесен в базовый тип операнда.

Хорошо, это не проблема, применяется неявное приведение (я думаю, что "cast" означает неявное или явное приведение). Но правило 10.1 гласит:

Значение выражения целочисленного типа не должно быть неявно преобразовано в другой базовый тип, выражение является сложным.

Предыдущий пример сложной операции: ~ u16a

Я меняю свой код на:

uint16 var1 = 1U;
uint16 var2 = (uint16) ~var1;

И я получаю другое предупреждение: я думаю, что преобразование значения int negative в значение unsigned int небезопасно. Я проверяю стандарт C99 (ISO C99) § 6.3.1.3, но я не понимаю, ясно ли указано преобразование int в unsigned short.

В статье EmbeddedGurus Я читал:

c = (unsigned int) a; /* Since a is positive, this cast is safe */

Мои вопросы:

  • Явное преобразование из подписанного int в неподписанное короткое неуказанное поведение?
  • Если да, как безопасно использовать оператор дополнения с unsigned short?

Ответ 1

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

На вашей платформе uint16_t стандартно поддерживается int, так как ваш int может представлять все значения a uint16_t. Затем побитовое отрицание применяется к этому значению int, что является причиной проблемы.

Чтобы получить детерминированный результат независимо от платформы, преобразуйте значение в unsigned int самостоятельно:

 uint16_t var2 = (uint16_t) ~((unsigned int) var1);

Обратите внимание, что это всегда правильно, так как unsigned int требуется для представления всех значений a uint16_t.

Ответ 2

  • Явное преобразование из подписанного int в неподписанное короткое неуказанное поведение?

Преобразование из подписанных значений без знака хорошо указано, что это происходит через modulo арифметику, охватывается секцией 6.3.1.3 Целочисленные и беззнаковые целые числа из стандартного проекта C99:

В противном случае, если новый тип без знака, значение преобразуется путем многократного добавления или вычитая одно больше максимального значения, которое может быть представлено в новом типе пока значение не окажется в диапазоне нового типа. 49)

Итак, для вашего случая отрицательное число будет конвертировано путем многократного добавления:

UMAX + 1 

к отрицательному результату, пока он не станет диапазоном беззнакового типа, к которому вы конвертируете.

Например, преобразование -1 в неподписанный тип всегда приводит к максимальному значению без знака, поскольку -1 + UMAX + 1 всегда UMAX.

  1. Если да, как безопасно использовать оператор дополнения с unsigned short?

Что происходит, когда вы применяете оператор ~, является то, что значение присваивается в int из-за применения целых рекламных акций к операнду ~. Что описано в разделе 6.5.3.3 Унарные арифметические операторы, в которых говорится (выделено мной):

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

Учитывая последнее предложение в цитируемом параграфе, возможно, приведение к unsigned int сначала приведет к более интуитивным результатам:

uint16 var2 = ~((unsigned int)var1);

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

uint16 var2 = (uint16) ~((unsigned int)var1);