Почему мой инструмент выдал здесь ошибку MISRA?

Что я могу сделать, чтобы MISRA не выдавала эту ошибку для кода ниже? Я пробовал кастинг с (unit16_t). Но тогда это не позволяло явное преобразование.

Недопустимое неявное преобразование базового типа MISRA «unsigned char» в «unsigned int» в сложном выражении (правило 10.1 MISRA C 2004)

 uint8_t rate = 3U; 
 uint8_t percentage = 130U;      
 uint16_t basic_units = rate * percentage;

person Ammamon    schedule 15.06.2011    source источник


Ответы (4)


Проблема заключается в том, что как ставка, так и процент молча продвигаются целочисленными акциями до типа «int». Таким образом, умножение выполняется над типом со знаком.

Код, совместимый с MISRA, должен либо переписать код как

 uint16_t basic_units = (uint16_t)rate * (uint16_t)percentage;

или сделайте, как предлагает MISRA, немедленно приведите результат выражения к его «базовому типу»:

 uint16_t basic_units = (uint8_t)(rate * percentage);

EDIT: следует пояснение.

ИСО 9899:1999 6.3.1.1 2

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

Информативный текст от MISRA-C:

MISRA-C:2004 6.10.3 Опасные преобразования типов:

/--/

– Изменение знака в арифметических операциях: целочисленное преобразование часто приводит к тому, что два операнда без знака дают результат типа (signed) int. Например, сложение двух 16-битных операндов без знака даст 32-битный результат со знаком, если int равен 32 битам, и 16-битный результат без знака, если int равен 16. биты.

На самом деле я не уверен, удовлетворит ли моя вторая строка выше MISRA или нет, если подумать, я, возможно, перепутал MISRA 10.1 с 10.5, где последний обеспечивает немедленное приведение к базовому типу, но только в случае определенных побитовых операторов .

Я протестировал обе строки с помощью статического анализа кода LDRA, и он не жаловался (но выдавал некоторые неверные, несвязанные предупреждения), но затем LDRA также очень плохо работает на MISRA-C.

В любом случае, проблема в исходном вопросе заключается в том, что как ставка, так и процент неявно преобразуются целочисленными рекламными акциями в тип int, который является подписанным, поскольку int может представлять все значения uint8_t. Так становится

uint16_t basic units = (int)rate * (int)percentage.

Чтобы предотвратить это, вы должны явно указать тип. Подумав еще раз, я бы выбрал 1-ю строку из двух вышеперечисленных.

person Lundin    schedule 15.06.2011
comment
Почему преобразование двух беззнаковых типов uint8_t приведет к появлению знакового типа int, а не большего беззнакового типа unsigned int или, проще говоря, uint16_t? - person Ioan; 15.06.2011
comment
@pmg Думаю, я действительно перепутал правила MISRA 10.1 и 10.5. Если бы это были операторы ~ или ‹‹, MISRA заставила бы меня преобразовать тип в базовый тип. Я отредактировал свой пост, чтобы прояснить ситуацию. - person Lundin; 16.06.2011
comment
@loan Потому что C - очень нелогичный язык. Никогда не думайте, что любое неявное преобразование, выполненное C, является разумным! :) Я отредактировал свой пост, чтобы объяснить дальше. - person Lundin; 16.06.2011

Неявное преобразование выполняется перед умножением, для умножения. Возможно, явное преобразование прямо перед тем, как умножение отключит ваш инструмент.

uint16_t basic_units = (unsigned)rate * (unsigned)percentage;

Полученное значение unsigned должно быть неявно преобразовано в uint16_t без предупреждений. Если ваш инструмент тоже хочет быть PITA в этом отношении, попробуйте другое явное преобразование:

uint16_t basic_units = (uint16_t)((unsigned)rate * (unsigned)percentage);
person pmg    schedule 15.06.2011
comment
Упс, чуть не выложил что-то идентичное. Однако обратите внимание, что MISRA не одобряет использование unsigned (int) в консультативных правилах, поэтому вам следует привести тип к uint16_t для максимального отключения инструментов. - person Lundin; 15.06.2011
comment
Хм, у меня нет правил MISRA :) Я просто подумал, что когда uint16_t имеет меньший ранг, чем int, проблема не исчезнет: тогда значения тоже будут преобразованы в больший тип unsigned. - person pmg; 15.06.2011
comment
В самом деле, если uint16_t окажется коротким без знака, он все равно будет повышен. Поэтому, возможно, лучший (и наиболее совместимый с MISRA) код - это приведение результата к типу, как во 2-й строке кода в моем ответе. - person Lundin; 15.06.2011
comment
@Lundin: Разве проблема не в преобразовании перед умножением? Ваша вторая строка кода неявно преобразует rate и percentage в unsigned перед операцией (и генерирует ошибку, как в вопросе ОП) - person pmg; 15.06.2011
comment
Спасибо Лундин и pmg. Я переписал код как uint16_t basic_units = (uint16_t)rate * (uint16_t)percentage;. Теперь у Мишры с этим все в порядке. - person Ammamon; 16.06.2011
comment
@Ammamon: обратите внимание, что с учетом int16_t foo(int16_t x) { if (should_fire_airbag()) fire_airbag()); return x*x;} компилятор для 32-битной системы может в соответствии со стандартом C генерировать код для foo(0xFF00);, который безоговорочно запускает подушку безопасности. Безопасными формами умножения будут либо (0u+x)*x, либо 1uxx. Обратите внимание, что компиляторы старше пяти лет, вероятно, не будут генерировать безусловный вызов fire_airbag, но современные агрессивные оптимизаторы больше заинтересованы в вырезании любого кода, который стандарт C не требует от них включать, чем в создании кода, который ведет себя так, как задумано. . - person supercat; 23.04.2015

Правило MISRA пытается гарантировать, что «базовый тип», используемый для расчета, совпадает с результирующим типом. Для этого вы можете привести один или оба операнда:

uint8_t rate = 3U; 
uint8_t percentage = 130U;      
uint16_t basic_units = (uint16_t)rate * percentage;

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

uint32_t rate =  ...;
uint32_t percentage = ...;
uint64_t basic_units = rate * percentage;

В 32-битной архитектуре операция будет выполняться в 32-битном формате, даже если целевой тип имеет ширину 64 бита. Если скорость и процент достаточно велики, это может привести к переносу операции на 32 бита, и поэтому данные, которые соответствовали бы целевому типу, будут потеряны.

Правило MISRA пытается сделать код более безопасным независимо от размера типов на целевой платформе.

person Richard Corden    schedule 09.02.2012

Если вы попробуете привести свои операнды, используя приведение в стиле C, вы можете просто дать своему инструменту что-то еще, на что можно пожаловаться. Итак, вместо этого:

uint16_t basic_units = (uint16_t)rate * (uint16_t)percentage;

Вам может понадобиться сделать это:

uint16_t basic_units = static_cast<uint16_t>(rate) * static_cast<uint16_t>(percentage);
person Big McLargeHuge    schedule 28.03.2018