Как назначить переменную битового поля переменной uint8_t, не нарушая правила MISRA? - PullRequest
4 голосов
/ 27 января 2020

У меня есть typedef struct с именем Character.

typedef struct {
    unsigned int a : 1;
    unsigned int b : 1;
    unsigned int c : 1;
    unsigned int d : 1;
    unsigned int o : 1;
    unsigned int p : 1;
    unsigned int q : 1;
    unsigned int x : 1;
} Character;

static Character tempChar;

void writeVar(const uint8_t *pData)
{
    tempChar.a = pData[0] >> 5;
    ...
}

Когда я пытаюсь присвоить переменную uin8_t (со значением 0 или 1) одному из этих битовых полей, я получил нарушение к правилу 10.6 MISRA, которое гласит:

Значение составного выражения не должно присваиваться объекту с более широким существенным типом

Есть ли способ присвоения битовое поле для uint8_t без нарушения MISRA C?

Ответы [ 4 ]

4 голосов
/ 27 января 2020

Оба операнда в выражении pData[0] >> 5 при необходимости будут повышаться до int (это произойдет для pData[0]).

И результатом выражения будет int.

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

Простое решение (как показано в комментариях) - явное преобразование pData[0] в unsigned int с использованием приведения.

0 голосов
/ 27 января 2020

Основная проблема здесь не имеет ничего общего с MISRA, но с попыткой сохранить значение в указанном c слоте в битовом поле. Вы не можете знать, как ваша схема расположения битовых полей в действительности оказывается в памяти, потому что это не определено в стандарте C.

Ваше битовое поле выделяет 8 битов значения в байте MS или байте LS? Это соответствует порядку байтов или нет? Что такое битовый порядок? Никто не знает. Шаг 1 - избавиться от битового поля.

Шаг 2 - избавиться от чего-либо unsigned int и использовать uint16_t / uint32_t.


Что касается В частности, MISRA- C 10.6, правило против неявного преобразования в более широкий тип всегда было ошибочным. Логическое обоснование MISRA, использованное для этого правила, заключалось в том, чтобы запретить людям писать код, подобный uint32_t u32 = u16a + u16b;, и думать, что операнд u32, равный =, каким-то волшебным образом означает, что операция будет выполняться на 32 битах вместо 16. Но на 8/16 битная система, она выполняется с 16-битной арифметикой c и могут быть переполнения / обходы.

Теперь, как это происходит, выполнение битовых сдвигов на знаковых типах всегда очень плохая идея. pData[0] получает неявное повышение до int, которое подписано. Есть и другие правила MISRA, а не те, которые вы цитировали.

Независимо от MISRA, вы должны всегда иметь привычку выполнять смены на неподписанных типах. «Это не опасно в этом случае» - унылое обоснование. Это означает, что всегда пишите (uint32_t)pData[0] >> 5, а приведение должно применяться до , а не после него. Это устраняет все неопределенности относительно неопределенного поведения левых сдвигов и потенциально арифметических c правых сдвигов и c. Пусть оптимизатор будет беспокоиться о фактическом используемом размере операндов оттуда.

0 голосов
/ 27 января 2020
tempChar.a = pData[0] >> 5;

В этом 5 это целочисленная константа со знаком. Вы должны использовать 5U для константы без знака

Кроме того, результатом операции сдвига вправо будет int Так что вам нужно набрать приведение результата обратно к unsigned int

tempChar.a = (unsigned int) (pData[0] >> 5U);
0 голосов
/ 27 января 2020

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

typedef union {

    struct {
        unsigned int a : 1;
        unsigned int b : 1;
        unsigned int c : 1;
        unsigned int d : 1;
        unsigned int o : 1;
        unsigned int p : 1;
        unsigned int q : 1;
        unsigned int x : 1;
    };

    uint8_t u8Value;

} Character;

И установить эти значения, обратившись к tempChar.u8Value вместо битовых полей. Например,

tempChar.Value |= (1 << 0);

установит tempChar.a в 1.

Это все равно сохранит аккуратность (читабельность) кода в той же степени. Например,

if(1 == tempChar.a)
{ 
    // Some code
}
...