Как получить массив бит в структуре?
Я размышлял (и поэтому искал способ узнать это, , а не лучшее решение), если можно получить массив бит в структуре.
Позвольте мне продемонстрировать на примере. Представьте такой код:
#include <stdio.h>
struct A
{
unsigned int bit0:1;
unsigned int bit1:1;
unsigned int bit2:1;
unsigned int bit3:1;
};
int main()
{
struct A a = {1, 0, 1, 1};
printf("%u\n", a.bit0);
printf("%u\n", a.bit1);
printf("%u\n", a.bit2);
printf("%u\n", a.bit3);
return 0;
}
В этом коде у нас есть 4 отдельных бита, упакованных в структуру. К ним можно получить доступ индивидуально, оставив работу манипуляции с битами в компиляторе. Мне было интересно, возможно ли такое:
#include <stdio.h>
typedef unsigned int bit:1;
struct B
{
bit bits[4];
};
int main()
{
struct B b = {{1, 0, 1, 1}};
for (i = 0; i < 4; ++i)
printf("%u\n", b.bits[i]);
return 0;
}
Я попробовал объявить bits
в struct B
как unsigned int bits[4]:1
или unsigned int bits:1[4]
или подобные вещи безрезультатно. Мое лучшее предположение было typedef unsigned int bit:1;
и использовать bit
как тип, но все же не работает.
Мой вопрос: возможно ли такое? Если да, то как? Если нет, почему бы и нет? 1 бит unsigned int является допустимым типом, поэтому почему бы вам не получить его?
Опять же, я не хочу замены для этого, мне просто интересно, как это возможно.
P.S. Я отмечаю это как С++, хотя код написан на C, потому что я предполагаю, что метод будет существовать на обоих языках. Если есть С++-специфический способ сделать это (используя языковые конструкции, а не библиотеки), мне также будет интересно узнать.
ОБНОВЛЕНИЕ: я полностью понимаю, что я могу выполнять бит операции самостоятельно. Я делал это тысячу раз в прошлом. Я НЕ заинтересован в ответе, который говорит, что вместо этого использует массив/вектор и выполняет манипуляции с битами. Я думаю только о том, возможно ли ЭТА КОНСТРУКЦИЯ или нет, НЕ альтернатива.
Обновление: ответ для нетерпеливых (спасибо neagoegab):
Вместо
typedef unsigned int bit:1;
Я мог бы использовать
typedef struct
{
unsigned int value:1;
} bit;
правильно используя #pragma pack
Ответы
Ответ 1
НЕ ВОЗМОЖНО - Конструкция вроде этого НЕ возможна (здесь) - НЕ ВОЗМОЖНО
Можно попытаться это сделать, но результат будет заключаться в том, что один бит хранится в одном байте
#include <cstdint>
#include <iostream>
using namespace std;
#pragma pack(push, 1)
struct Bit
{
//one bit is stored in one BYTE
uint8_t a_:1;
};
#pragma pack(pop, 1)
typedef Bit bit;
struct B
{
bit bits[4];
};
int main()
{
struct B b = {{0, 0, 1, 1}};
for (int i = 0; i < 4; ++i)
cout << b.bits[i] <<endl;
cout<< sizeof(Bit) << endl;
cout<< sizeof(B) << endl;
return 0;
}
выход:
0 //bit[0] value
0 //bit[1] value
1 //bit[2] value
1 //bit[3] value
1 //sizeof(Bit), **one bit is stored in one byte!!!**
4 //sizeof(B), ** 4 bytes, each bit is stored in one BYTE**
Для доступа к отдельным битам из байта приведен пример (обратите внимание, что макет битовых полей зависит от реализации)
#include <iostream>
#include <cstdint>
using namespace std;
#pragma pack(push, 1)
struct Byte
{
Byte(uint8_t value):
_value(value)
{
}
union
{
uint8_t _value;
struct {
uint8_t _bit0:1;
uint8_t _bit1:1;
uint8_t _bit2:1;
uint8_t _bit3:1;
uint8_t _bit4:1;
uint8_t _bit5:1;
uint8_t _bit6:1;
uint8_t _bit7:1;
};
};
};
#pragma pack(pop, 1)
int main()
{
Byte myByte(8);
cout << "Bit 0: " << (int)myByte._bit0 <<endl;
cout << "Bit 1: " << (int)myByte._bit1 <<endl;
cout << "Bit 2: " << (int)myByte._bit2 <<endl;
cout << "Bit 3: " << (int)myByte._bit3 <<endl;
cout << "Bit 4: " << (int)myByte._bit4 <<endl;
cout << "Bit 5: " << (int)myByte._bit5 <<endl;
cout << "Bit 6: " << (int)myByte._bit6 <<endl;
cout << "Bit 7: " << (int)myByte._bit7 <<endl;
if(myByte._bit3)
{
cout << "Bit 3 is on" << endl;
}
}
Ответ 2
В С++ вы используете std::bitset<4>
. Это будет использовать минимальное количество слов для хранения и скрыть все маскирование от вас. Очень сложно отделить библиотеку С++ от языка, потому что большая часть языка реализована в стандартной библиотеке. В C нет прямого способа создания массива одиночных битов, подобных этому, вместо этого вы создадите один элемент из четырех бит или выполните манипуляцию вручную.
EDIT:
1 бит unsigned int является допустимым типом, поэтому почему бы вам не удастся получить его массив?
На самом деле вы не можете использовать 1-битный беззнаковый тип в любом месте, кроме контекста создания члена struct/class. В этот момент он настолько отличается от других типов, что автоматически не следует, что вы можете создать массив из них.
Ответ 3
С++ использовал бы std::vector<bool>
или std::bitset<N>
.
В C, чтобы эмулировать семантику std::vector<bool>
, вы используете такую структуру:
struct Bits {
Word word[];
size_t word_count;
};
где Word
- тип реализации, равный по ширине шине данных ЦП; wordsize
, как используется позже, равно ширине шины данных.
например. Word
- uint32_fast_t
для 32-разрядных машин, uint64_fast_t
для 64-разрядных машин;
wordsize
- 32 для 32-разрядных машин и 64 для 64-разрядных машин.
Вы используете функции/макросы для установки/очистки битов.
Чтобы извлечь бит, используйте GET_BIT(bits, bit) (((bits)->)word[(bit)/wordsize] & (1 << ((bit) % wordsize)))
.
Чтобы установить бит, используйте SET_BIT(bits, bit) (((bits)->)word[(bit)/wordsize] |= (1 << ((bit) % wordsize)))
.
Чтобы очистить бит, используйте CLEAR_BIT(bits, bit) (((bits)->)word[(bit)/wordsize] &= ~(1 << ((bit) % wordsize)))
.
Чтобы перевернуть бит, используйте FLIP_BIT(bits, bit) (((bits)->)word[(bit)/wordsize] ^= (1 << ((bit) % wordsize)))
.
Чтобы добавить размерность по std::vector<bool>
, создайте функцию изменения размера, которая вызывает realloc
на Bits.word
и соответственно изменит Bits.word_count
. Точные сведения об этом оставлены как проблема.
То же самое относится к правильной проверке диапазонов индексов бит.
Ответ 4
Это оскорбительно и полагается на расширение... но это сработало для меня:
struct __attribute__ ((__packed__)) A
{
unsigned int bit0:1;
unsigned int bit1:1;
unsigned int bit2:1;
unsigned int bit3:1;
};
union U
{
struct A structVal;
int intVal;
};
int main()
{
struct A a = {1, 0, 1, 1};
union U u;
u.structVal = a;
for (int i =0 ; i<4; i++)
{
int mask = 1 << i;
printf("%d\n", (u.intVal & mask) >> i);
}
return 0;
}
Ответ 5
Вы также можете использовать массив целых чисел (ints или longs) для сборки произвольно большой битовой маски. Системный вызов select() использует этот подход для своего типа fd_set; каждый бит соответствует нумерованному файловому дескриптору (0..N). Макросы определены: FD_CLR для очистки бит, FD_SET для установки бит, FD_ISSET для тестирования бит, а FD_SETSIZE - общее количество бит. Макросы автоматически определяют, какое целое число в массиве нужно получить, а какой бит - в целочисленном. В Unix см. "Sys/select.h"; под Windows, я думаю, что это в "winsock.h". Вы можете использовать технику FD для создания собственных определений для битовой маски. В С++ я предполагаю, что вы можете создать объект битовой маски и перегрузить оператор [] для доступа к отдельным битам.
Ответ 6
Вы можете создать список бит с помощью указателя struct. Это будет использовать больше, чем бит места на бит, написанный, хотя, поскольку он будет использовать один байт (для адреса) на бит:
struct bitfield{
unsigned int bit : 1;
};
struct bitfield *bitstream;
Затем после этого:
bitstream=malloc( sizeof(struct bitfield) * numberofbitswewant );
Вы можете получить к ним доступ так:
bitstream[bitpointer].bit=...