Подтвердить что ты не робот

Есть ли способ доступа к отдельным битам с объединением?

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

typedef union 
{
    unsigned char status;
    bit bits[8];
}DeviceStatus;

но компилятору это не нравится. По-видимому, вы не можете использовать биты в структуре. Так что я могу сделать вместо этого?

4b9b3361

Ответ 1

Конечно, но вы действительно хотите использовать struct для определения таких битов как

typedef union
{
  struct
  {
    unsigned char bit1 : 1;
    unsigned char bit2 : 1;
    unsigned char bit3 : 1;
    unsigned char bit4 : 1;
    unsigned char bit5 : 1;
    unsigned char bit6 : 1;
    unsigned char bit7 : 1;
    unsigned char bit8 : 1;
  }u;
  unsigned char status;
}DeviceStatus;

Затем вы можете получить доступ для DeviceStatus ds;, к которому вы можете получить доступ ds.u.bit1. Кроме того, некоторые компиляторы фактически позволят вам иметь анонимные структуры внутри объединения, так что вы можете просто получить доступ к ds.bit1, если вы опустите u из typedef.

Ответ 2

У вас есть пара возможностей. Можно было бы просто использовать логику Boolean для получения бит:

int bit0 = 1;
int bit1 = 2;
int bit2 = 4;
int bit3 = 8;
int bit4 = 16;
int bit5 = 32;
int bit6 = 64;
int bit7 = 128;

if (status & bit1)
    // whatever...

Другим является использование битовых полей:

struct bits { 
   unsigned bit0 : 1;
   unsigned bit1 : 1;
   unsigned bit2 : 1;
// ...
};

typedef union {
    unsigned char status;
    struct bits bits;
} status_byte;

some_status_byte.status = whatever;
if (status_byte.bits.bit2)
    // whatever...

Первый (по крайней мере, возможно) более портативный, но когда вы имеете дело со битами состояния, есть вероятность, что код даже не портативен, так что вам может быть не все равно...

Ответ 3

Как уже было сказано, вы не можете адресовать память меньше байта в C. Я бы написал макрос:

#define BIT(n) (1 << n)

и использовать его для доступа к битам. Таким образом, ваш доступ будет таким же, независимо от размера структуры, к которой вы обращаетесь. Вы должны написать свой код как:

if (status & BIT(1)) {
   // Do something if bit 1 is set
} elseif (~status | BIT(2) {
   // Do something else if bit 2 is cleared
} else  {
   // Set bits 1 and 2
   status |= BIT(1) | BIT(2)
   // Clear bits 0 and 4
   status &= ~(BIT(0) | BIT(4))
   // Toggle bit 5 
   status ^= BIT(5)
}

Это даст вам доступ к вашей предлагаемой системе, которая будет использовать [] вместо().

Ответ 4

typedef union
{
  unsigned char status;
  struct bitFields
  {
    _Bool bit0 : 1;
    _Bool bit1 : 1;
    _Bool bit2 : 1;
    _Bool bit3 : 1;
    _Bool bit4 : 1;
    _Bool bit5 : 1;
    _Bool bit6 : 1;
    _Bool bit7 : 1;
  } bits;
}DeviceStatus;

Ответ 5

Самый маленький блок, адресуемый в C, всегда является байтом (называемый char в C). Вы не можете получить доступ напрямую. Ближайшим способом получить доступ к битам будет определение типа данных, называемого bitpointer, и определение для него некоторых функций или макросов:

#include <stdbool.h>

typedef struct bitpointer {
    unsigned char *pb; /* pointer to the byte */
    unsigned int bit; /* bit number inside the byte */
} bitpointer;

static inline bool bitpointer_isset(const bitpointer *bp) {
    return (bp->pb & (1 << bp->bit)) != 0;
}

static inline void bitpointer_set(const bitpointer *bp, bool value) {
    unsigned char shifted = (value ? 1 : 0) << bp->bit;
    unsigned char cleared = *bp->pb &~ (1 << bp->bit);
    *(bp->pb) = cleared | shifted;
}

Я рекомендую против союзов, потому что он определяется реализацией, будут ли они заполнены msb-to-lsb или lsb-to-msb (см. ISO C99, 6.7.2.1p10).

Ответ 6

Вы можете сделать это, поместив биты в структуру внутри объединения, но она может работать или не работать, в зависимости от вашей реализации. Определение языка не указывает, в каком порядке отдельные биты будут сопоставляться с битами unsigned char; хуже того, он даже не гарантирует, что бит будет перекрываться с unsigned char (компилятор может решить разместить отдельные биты в сторону наиболее значимой стороны слова и unsigned char по направлению к наименее значимой стороне или наоборот).

Обычная техника в вашей ситуации - использовать побитовые операции. Определите константы, названные в честь значения битов, например,

#define FLAG_BUSY 0x01
#define FLAG_DATA_AVAILABLE 0x02
#define FLAG_TRANSMISSION_IN_PROGRESS 0x04
...
#define FLAG_ERROR 0x80

Затем для чтения и записи отдельных битов:

if (status & FLAG_BUSY) ... /* test if the device is busy */
status &= ~FLAG_ERROR; /* turn off error flag */
status |= FLAG_TRANSMISSION_IN_PROGRESS /* turn on transmission-in-progress flag */