Как получить массив битов в структуре? - PullRequest
27 голосов
/ 16 ноября 2011

Я размышлял (и поэтому ищу способ узнать это , а не лучшее решение ), если возможно получить массив битов в структуре.

Позвольте мне продемонстрировать на примере. Представьте себе такой код:

#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 ++, хотя код написан на C, потому что я предполагаю, что метод будет существовать на обоих языках. Если есть особый способ сделать это на C ++ (используя языковые конструкции, а не библиотеки), мне также было бы интересно узнать.

ОБНОВЛЕНИЕ: я полностью осознаю, что могу выполнять битовые операции самостоятельно. Я делал это тысячу раз в прошлом. Я НЕ заинтересован в ответе, который говорит использовать массив / вектор вместо этого и делать битовые манипуляции. Я только думаю, если ЭТОГО КОНСТРУКЦИИ возможно или нет, а не альтернатива.

Обновление: ответ для нетерпеливых (спасибо neagoegab):

вместо

typedef unsigned int bit:1;

Я мог бы использовать

typedef struct
{
    unsigned int value:1;
} bit;

правильно используя #pragma pack

Ответы [ 6 ]

9 голосов
/ 16 ноября 2011

НЕ ВОЗМОЖНО - Такая конструкция НЕ ВОЗМОЖНА ( здесь ) - НЕ ВОЗМОЖНО

Можно попытаться сделать это, но в результате один бит будет храниться в одном байте

#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;
    }
}
6 голосов
/ 16 ноября 2011

В C ++ вы используете std::bitset<4>.Это будет использовать минимальное количество слов для хранения и скрыть всю маскировку от вас.Очень трудно отделить библиотеку C ++ от языка, потому что большая часть языка реализована в стандартной библиотеке.В C нет прямого способа создать массив из отдельных битов, как этот, вместо этого вы бы создали один элемент из четырех битов или выполняли манипуляции вручную.

EDIT:

1-битный беззнаковый тип int является допустимым типом, так почему вы не можете получить его массив?

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

4 голосов
/ 16 ноября 2011

C ++ будет использовать 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.Точная информация об этом остается проблемой.

То же самое относится к правильной проверке диапазона битовых индексов.

2 голосов
/ 04 мая 2015

это оскорбительно и зависит от расширения ... но у меня это сработало:

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;
}
0 голосов
/ 19 января 2016

Вы можете создать список битов, используя указатель структуры.Это будет использовать больше, чем бит на записанный бит, поскольку он будет использовать один байт (для адреса) на бит:

struct bitfield{
    unsigned int bit : 1;
};
struct bitfield *bitstream;

Затем после этого:

bitstream=malloc( sizeof(struct bitfield) * numberofbitswewant );

Вы можете получить к ним доступ так:

bitstream[bitpointer].bit=...
0 голосов
/ 16 ноября 2011

Вы также можете использовать массив целых чисел (целых или длинных) для построения произвольно большой битовой маски. Системный вызов select () использует этот подход для своего типа fd_set; каждый бит соответствует пронумерованному дескриптору файла (0..N). Макросы определены: FD_CLR для сброса бита, FD_SET для установки бита, FD_ISSET для проверки бита и FD_SETSIZE - общее количество битов Макросы автоматически определяют, к какому целому числу в массиве обращаться, а к какому биту в целом. В Unix смотрите "sys / select.h"; под виндой я думаю это в "winsock.h". Вы можете использовать технику FD, чтобы сделать свои собственные определения для битовой маски. В C ++, я полагаю, вы можете создать объект битовой маски и перегрузить оператор [] для доступа к отдельным битам.

...