Хранение 8 логических значений true/false внутри 1 байта?

Я работаю над микроконтроллером только с 2 КБ SRAM и отчаянно нуждаюсь в сохранении некоторой памяти. Попытка выяснить, как я могу поместить значения 8 0/1 в один байт с использованием битового поля, но не могу его полностью реализовать.

struct Bits
{
    int8_t b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1;
};

int main(){
    Bits b;
    b.b0 = 0;
    b.b1 = 1;

    cout << (int)b.b0; // outputs 0, correct
    cout << (int)b.b1; // outputs -1, should be outputting 1
}

Что дает?

Ответ 1

Все ваши члены битового поля подписываются 1-битными целыми числами. В системе с двумя дополнениями это означает, что они могут представлять только либо 0, либо -1. Используйте uint8_t, если вы хотите 0 и 1:

struct Bits
{
    uint8_t b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1;
};

Ответ 2

В качестве предостережения - стандарт фактически не применяет схему реализации для битполей. Нет гарантии, что Bits будет 1 байт, и гипотетически вполне возможно, что он будет больше.

На практике, однако, фактические реализации обычно следуют очевидной логике, и она будет "почти всегда" размером 1 байт, но опять же, нет требования, чтобы она была гарантирована. На всякий случай, если вы хотите быть уверенным, вы можете сделать это вручную.

BTW -1 по-прежнему true, но он -1 != true

Ответ 3

Как уже отмечалось, эти переменные состоят только из знакового бита, поэтому единственными доступными значениями являются 0 и -1.

Более подходящий тип для этих битовых полей будет bool. С++ 14 §9.6/4:

Если значение true или false сохраняется в бит-поле типа bool любого размера (включая однобитовое битовое поле), исходное значение bool и значение бит поле сравнивается равным.

Да, std::uint8_t выполнит эту работу, но вы также можете использовать наилучшую посадку. Вам не понадобятся такие вещи, как бросок для std::cout << (int)b.b0;.

Ответ 4

Целочисленные и беззнаковые целые числа являются ответом.

Имейте в виду, что передача сигналов - это просто интерпретация бит, -1 или 1 - это просто сериализатор "print", интерпретирующий "тип переменной", поскольку он был "обнаружен" для функций cout (перегрузка операторов) компилятором, бит тот же, его значение также (вкл/выкл) - так как у вас всего 1 бит.

Не заботьтесь об этом, но это хорошая практика, чтобы быть явным, поэтому предпочитайте объявлять свою переменную без знака, она инструктирует компилятор установить правильный код, когда вы устанавливаете или получаете значение для сериализатора типа "print" (cout).

"COUT" ПЕРЕГРУЗКА ОПЕРАТОРА: "cout" работает через ряд функций, которые перегружает параметр инструкциям компилятора, который вызывает функцию. Таким образом, есть две функции: один получает неподписанные и другие подписанные, таким образом, они могут интерпретировать одни и те же данные по-разному, и вы можете изменить их, указав компилятору называть другой, используя приведение. См. cout < MyClass