Как последовательные битовые поля преобразуются в переменную при приведении - PullRequest
0 голосов
/ 04 апреля 2019

Я хотел бы использовать флаги в некоторой структуре, скажем:

struct {
    flag1:1;
    flag2:1;
    flag3:1;
    flag4:1;
}Flags;

Флаги 3 и 4 описывают режим работы моего целевого проекта в настоящее время.Я хотел бы преобразовать флаги в число, чтобы прочитать то, что было установлено в flag3 и flag4.Что я делаю:

uint8_t mode;
mode = ( ((uint8_t)Flags.flag4 << 1) | (uint8_t)Flags.flag3) );

Что меня удивляет, так это работает:

, если flag4 == 1 и flag3 == 1 'mode' равно 3

, еслиflag4 == 1 и flag3 == 0 'mode' равен 2 и т. д.

Мой вопрос: почему?Я сделал это с надеждой, что это сработает, но я не знаю почему.

Я работаю в Atollic для кода STM32.

Ответы [ 4 ]

1 голос
/ 04 апреля 2019

Потому что flag4 и flag3 будут интерпретироваться как цифры в двоичном числе.flag4 будет самым значительным.

Flags.flag4 << 1 будет сдвигать биты flag4 влево, поэтому, если flag4 равен 1, результат равен 10. Затем мы выполняем логическое или с flag3.Если flag3 равен 1, это даст 11, что равно 3 в двоичном виде.

Вот небольшой цикл, который может объяснить это.Допустим, у нас есть 8 флагов в массиве int flags[8].Теперь мы хотим сжать их в uint8_t flagfield.Это можно сделать с помощью этого цикла:

flagfield=0;
for(int i=0; i<8; i++)
    flagfield |= (uint8_t)(flags[i] << i);

flagfield теперь будет содержать все флаги.Если вы хотите, вы можете интерпретировать его как обычное целое число.

0 голосов
/ 04 апреля 2019

mode = ( ((uint8_t)Flags.flag4 << 1) | (uint8_t)Flags.flag3) );

когда:

, если flag4 == 1 и flag3 == 0 'mode' равен 2

Объяснение:

Flags.flag4 == 1
1 << 1 == 2
2 | 0 == 2

если flag4 == 1 и flag3 == 1 'mode' равен 3

Flags.flag4 == 1
1 << 1 == 2
2 | 1 == 3

Нужна хорошая книга для начинающих C

0 голосов
/ 04 апреля 2019

Позволяет удалить код:

mode = (((uint8_t) Flags.flag4 << 1) | (uint8_t) Flags.flag3)); </p>

Первый:

(uint8_t)Flags.flag4
(uint8_t)Flags.flag3

Flags.flag4 и Flags.flag3 приводятся к uint8_t.Эта часть важна.В то время как Flags.flag4 и Flags.flag3 имеют только один бит, преобразование преобразует его в полные 8 бит.

Next:

((uint8_t)Flags.flag4 << 1)

Это сдвигает значение flag4 на 1 бит влево,Это так же, как x * 2.Помните, что актеры превратили это в целые 8 битов.Таким образом, сдвиг не переполняется.flag4 может быть только 0 или 1, поэтому после сдвига это будет 0 или 2.

Последнее два значения объединяются в одну.Это дает результат 0-3 в зависимости от значений flag3 и flag4.

Обратите внимание на альтернативный способ написать это, хотя для архитектуры / ABI характерно использование

struct {
    uint8_t flag1:1;
    uint8_t flag2:1;
    union {
        struct {
            uint8_t flag3:1;
            uint8_t flag4:1;
        };
        uint8_t flag34:2;
    };
} Flags;

Порядокflag3 и flag4 в памяти разделены в соглашениях о вызовах архитектуры / ABI, поэтому flag34 не так переносим.Возможно, вам придется изменить флаги, чтобы получить оптимальный результат (самая короткая / самая быстрая сборка).

0 голосов
/ 04 апреля 2019

Когда flag4 равно 1, а flag3 равно 1, flag4 << 1 равно 10 (2 в десятичном виде), потому что вы сдвигаете 1 влево. С | вы добавляете flag3, который не сдвигается, поэтому у вас есть 11 (3 в десятичном виде). ​​

Когда flag3 равен 0, это делает то же самое, но результат равен 10 (2 в десятичном виде), потому что flag3 равен 0.

...