Нарушение правила 10.3 MISRA C-2012 из-за добавления двух 8-битных переменных, что привело к 32-битному

Я получаю нарушение правила 10.3 MISRA C-2012: неявное преобразование «var4 + var5» из 32-битного целого со знаком основного типа в другой или более узкий 8-битный беззнаковый тип основного типа для приведенного ниже кода.

int8_t var4 = -10;
uint8_t var5 = 15;
uint8_t var6 = var4 + var5;

Как «var4 + var5» интерпретируется как 32-битное целое число, поскольку каждый из них имеет 8-битный размер.

А также, каков правильный способ исправить это?


person Salim    schedule 28.11.2017    source источник
comment
См. также: stackoverflow.com/questions/6359404/   -  person Andrew    schedule 02.12.2017


Ответы (2)


Это происходит из-за целочисленных рекламных акций. Это подробно описано в разделе 6.3.1.1 стандарта C.

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

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

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

Поскольку int8_t и uint8_t имеют более низкий ранг, чем int, переменные этих типов повышаются до int при использовании в выражении. В случае:

uint8_t var6 = var4 + var5;

И var4, и var5 повышаются до int, и результат оператора + также имеет тип int. Это значение затем присваивается обратно var6, что является uint8_t, что может привести к усечению значения.

Лучший способ справиться с этим — сначала убедиться, что результат не может переполниться, а затем явно привести результат:

uint8_t var6 = (uint8_t)(var4 + var5);
person dbush    schedule 28.11.2017
comment
сначала убедитесь, что результат не может переполниться: точнее: если результат может переполнить тип int, это будет неопределенное поведение, поскольку операция представляет собой сложение целых чисел со знаком, но, учитывая диапазон операндов, это не может случаться. И наоборот, если результат выходит за пределы диапазона uint8_t, приведение его к (uint8_t) точно определено и действительно является неявным, но может не иметь ожидаемого эффекта, о котором вас предупреждает нарушение правила MISRA. - person chqrlie; 29.11.2017
comment
(uint8_t)(var4 + var5); нарушает правило 10.4 MISRA-C:2012: оба операнда оператора, в котором выполняются обычные арифметические преобразования, должны иметь одну и ту же категорию существенного типа. - person Lundin; 29.11.2017

Причина заключается в продвижении неявных типов. В этой единственной строке есть 3 неявных продвижения, что является опасным и неаккуратным стилем. У вас есть как целочисленное продвижение/обычные арифметические преобразования, так и неявное преобразование lvalue в одной строке кода.

Вы должны понимать, как эти покрытия работают в C, чтобы писать код C без ошибок, но вы также должны понимать их, чтобы использовать MISRA-C. На самом деле, одной из целей MISRA-C всегда было обучение программистов неявному продвижению типов — вы также найдете объяснения в документе MISRA-C.

Ваш код фактически нарушает несколько правил MISRA-C:2012. Не только 10.3, который касается неявных преобразований lvalue, но и 10.4:

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

var4 по существу подписано, а var5 по существу беззнаково. Поэтому недостаточно просто привести результат к соответствующему типу. Чтобы получить код, совместимый с MISRA-C, вы должны привести оба операнда к одному и тому же типу. Или лучше использовать для начала переменные соответствующего типа: нет особого смысла хранить результат -10 + 15 в переменной без знака. Либо вам нужны подписанные номера, либо нет.

Чтобы получить код, совместимый с MISRA-C, который также является полностью переносимым, это должен быть тип, который не может быть неявно повышен, например uint32_t.

uint8_t var6 = (uint8_t) ((uint32_t)var4 + (uint32_t)var5);

альтернативно

int8_t var6 = (int8_t) ((int32_t)var4 + (int32_t)var5);

Это выражение является жестким, профессиональным C, поскольку оно не содержит ни одного неявного расширения. Это самодокументирующийся код, так как он показывает, что программист знает и учёл неявные продвижения типов. Это не влияет на производительность по сравнению с выражением только с 8-битными операндами, поскольку компилятор может оптимизировать его.

person Lundin    schedule 29.11.2017