Нарушение Misra с побитовым оператором

Я написал следующий фрагмент кода, который не нравится MISRA:

UartPtr->C &= ((uint8_t)(~SIO_C2_SBK));

с

#define SIO_C2_SBK ((uint8_t)0x01u)

и UartPtr определяется как

UartPtr = (UartStruct*) 0x12345678; /* I know that this is also a violation of MISRA */

с базовой структурой данных:

typedef volatile struct UartStructTag
{
  uint8_t      BDH;
  uint8_t      BDL;
  uint8_t      C1;
  uint8_t      C2;
} UartStruct;

Мой чекер Misra жалуется на первую строчку и констатирует, что

Целочисленное константное выражение с отрицательным значением преобразуется в тип без знака.

Однако следующая строка не вызывает проблем с MISRA:

UartPtr->C |= ((uint8_t)(SIO_C2_SBK));

Таким образом, проблема возникает из-за побитового отрицания. Но так как все операции напрямую приводятся к uint8_t, я не вижу нарушения стандарта MISRA. Кто хочет помочь мне здесь?


person m47h    schedule 02.12.2015    source источник
comment
Что ж, ~SIO_C2_SBK — отрицательное значение, так что сообщение фактически верно.   -  person M.M    schedule 02.12.2015
comment
Какая версия МИСРА? Какой инструмент вы используете?   -  person Lundin    schedule 02.12.2015
comment
@Lundin: я использую QAC 7 с MISRA 2004.   -  person m47h    schedule 02.12.2015


Ответы (2)


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

UartPtr->C &= ((uint8_t)(~(int)(uint8_t)0x01u));

что просто

UartPtr->C &= ((uint8_t)(~1));

где ~1 имеет значение -2 в двух дополнительных архитектурах.

Чтобы устранить эту проблему, преобразуйте в unsigned или любой другой тип без знака, больший, чем int, прежде чем применять побитовое значение not:

UartPtr->C &= ((uint8_t)(~(unsigned)SIO_C2_SBK));
person fuz    schedule 02.12.2015
comment
Такова причина этого правила, что если, например. SIO_C2_SBK будет константой с отрицательным знаком, операция в конечном итоге не даст желаемого результата, и приведение скроет это? - person m47h; 02.12.2015
comment
@ m47h Правило скорее пытается защитить вас от таких ситуаций, как if(~SIO_C2_SBK > 0), которые ведут себя не так, как ожидалось. Это действительно неприятная ошибка, которую нужно отследить. - person Lundin; 02.12.2015

Оператор ~, как и большинство операторов C, выполняет неявное целочисленное преобразование операнда перед применением оператора.

#define SIO_C2_SBK ((uint8_t)0x01u)

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

Это нарушает правило 10.1 MISRA-C:2004, которое не разрешает неявные преобразования, дающие тип с другой подписью (такие преобразования опасны, так что это очень хорошее правило).

  • Если вам не нужен этот макрос, чтобы дать uint8_t, просто отбросьте приведение (uint8_t, и это решит проблему.

  • Если этот макрос по какой-то причине должен выдавать uint8_t, измените код на этот (совместимый с MISRA):

    UartPtr->C &= (uint8_t) ~(uint32_t)SIO_C2_SBK;
    

    где uint32_t соответствует размеру int на данной платформе.

person Lundin    schedule 02.12.2015
comment
Я не очень хорошо знаком с MISRA, но разве он не требует маскирования с помощью & 0xFF перед приведением с помощью uint8_t? - person user694733; 02.12.2015
comment
@ user694733 Нет. Маскирование с помощью & 0xFF по сути то же самое, что и приведение к uint8_t. Однако MISRA 10.5 явно требует, чтобы вы приводили результат оператора ~ к предполагаемому типу. - person Lundin; 02.12.2015