Поиск…


Вступление

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

Синтаксис

  • Идентификатор спецификатора типа: размер;

параметры

параметр Описание
Тип Спецификатор signed , unsigned , int или _Bool
идентификатор Имя этого поля в структуре
размер Количество бит для использования в этом поле

замечания

Единственные переносные типы для бит-полей - это signed , unsigned или _Bool . Можно использовать простой тип int , но стандарт говорит (§6.7.2¶5) ... для бит-полей, он определяется реализацией, специфицирует ли спецификатор int тот же тип, что и signed int или тот же тип, что и unsigned int .

Другие целые типы могут быть разрешены конкретной реализацией, но использование их не переносимо.

Битовые поля

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

struct encoderPosition {
   unsigned int encoderCounts : 23;
   unsigned int encoderTurns  : 4;
   unsigned int _reserved     : 5;
};

В этом примере мы рассмотрим кодировщик с 23 битами одинарной точности и 4 битами для описания многооборотных. Бит-поля часто используются при взаимодействии с оборудованием, которое выводит данные, связанные с определенным количеством бит. Другим примером может быть связь с FPGA, где FPGA записывает данные в вашу память в 32-разрядных разделах, что позволяет использовать аппаратное обеспечение:

struct FPGAInfo {
    union {
        struct bits {
            unsigned int bulb1On  : 1;
            unsigned int bulb2On  : 1;
            unsigned int bulb1Off : 1;
            unsigned int bulb2Off : 1;
            unsigned int jetOn    : 1;
        };
        unsigned int data;
   };
};

В этом примере мы показали широко используемую конструкцию, чтобы иметь возможность доступа к данным в своих отдельных битах или для записи пакета данных в целом (эмулируя то, что может сделать FPGA). Затем мы могли бы получить доступ к битам следующим образом:

FPGAInfo fInfo;
fInfo.data = 0xFF34F;
if (fInfo.bits.bulb1On) {
    printf("Bulb 1 is on\n");
}

Это действительно, но согласно стандарту C99 6.7.2.1, пункт 10:

Порядок распределения бит-полей внутри единицы (от высокого порядка до младшего или низкого порядка) определяется реализацией.

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

typedef union {
    struct bits {
#if defined(WIN32) || defined(LITTLE_ENDIAN)
    uint8_t commFailure :1;
    uint8_t hardwareFailure :1;
    uint8_t _reserved :6;
#else
    uint8_t _reserved :6;
    uint8_t hardwareFailure :1;
    uint8_t commFailure :1;
#endif
    };
    uint8_t data;
} hardwareStatus;

Использование битовых полей в виде небольших целых чисел

#include <stdio.h>

int main(void)
{
    /* define a small bit-field that can hold values from 0 .. 7 */
    struct
    {
        unsigned int uint3: 3;
    } small;

    /* extract the right 3 bits from a value */
    unsigned int value = 255 - 2; /* Binary 11111101 */
    small.uint3 = value;          /* Binary      101 */
    printf("%d", small.uint3);

    /* This is in effect an infinite loop */
    for (small.uint3 = 0; small.uint3 < 8; small.uint3++)
    {
        printf("%d\n", small.uint3);
    }

    return 0;
}

Выравнивание битового поля

Бит-поля дают возможность объявлять поля структуры, которые меньше ширины символов. Бит-поля реализуются с помощью маски уровня байта или уровня слова. Следующий пример приводит к структуре из 8 байтов.

struct C
{
    short s;            /* 2 bytes */
    char  c;            /* 1 byte */
    int   bit1 : 1;     /* 1 bit */
    int   nib  : 4;     /* 4 bits padded up to boundary of 8 bits. Thus 3 bits are padded */
    int   sept : 7;     /* 7 Bits septet, padded up to boundary of 32 bits. */
};

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

Безымянное битовое поле может иметь любой размер, но их нельзя инициализировать или ссылаться.

Битовому полю с нулевой шириной нельзя присвоить имя и выровнять следующее поле с границей, определяемой типом данных бит-поля. Это достигается путем заполнения битов между битовыми полями.

Размер структуры «A» равен 1 байт.

struct A
{
    unsigned char c1 : 3;
    unsigned char c2 : 4;
    unsigned char c3 : 1;
};

В структуре B первое неназванное битовое поле пропускает 2 бита; бит-поле нулевой ширины после c2 заставляет c3 начинаться с границы символа (так что 3 бита пропускаются между c2 и c3 . После c4 есть 3 бита заполнения. Таким образом, размер структуры составляет 2 байта.

struct B
{
    unsigned char c1 : 1;
    unsigned char    : 2;    /* Skips 2 bits in the layout */
    unsigned char c2 : 2;
    unsigned char    : 0;    /* Causes padding up to next container boundary */ 
    unsigned char c3 : 4;
    unsigned char c4 : 1;
};

Когда поле бит полезно?

Битовое поле используется для объединения многих переменных в один объект, аналогичный структуре. Это позволяет сократить использование памяти и особенно полезно во встроенной среде.

 e.g. consider the following variables having the ranges as given below.
 a --> range 0 - 3
 b --> range 0 - 1
 c --> range 0 - 7
 d --> range 0 - 1
 e --> range 0 - 1

Если мы объявляем эти переменные отдельно, каждый из них должен быть как минимум 8-битным целым числом, а требуемое общее пространство будет 5 байтов. Кроме того, переменные не будут использовать весь диапазон 8-битного беззнакового целого числа (0-255). Здесь мы можем использовать бит-поля.

typedef struct {
   unsigned int a:2;
   unsigned int b:1;
   unsigned int c:3;
   unsigned int d:1;
   unsigned int e:1;
} bit_a;

Битовые поля в структуре доступны так же, как и любая другая структура. Программисту необходимо следить за тем, чтобы переменные записывались в диапазоне. Если вне диапазона, поведение не определено.

int main(void)
{
   bit_a bita_var;
   bita_var.a = 2;              // to write into element a
   printf ("%d",bita_var.a);    // to read from element a.
   return 0;
}

Часто программист хочет обнулить набор бит-полей. Это можно сделать по элементам, но есть второй метод. Просто создайте объединение структуры выше с неподписанным типом, который больше или равен размеру структуры. Тогда весь набор бит-полей может быть обнулен путем обнуления этого целого без знака.

typedef union {
    struct {
       unsigned int a:2;
       unsigned int b:1;
       unsigned int c:3;
       unsigned int d:1;
       unsigned int e:1;
    };
    uint8_t data;
} union_bit;    

Использование выглядит следующим образом

int main(void)
{
   union_bit un_bit;
   un_bit.data = 0x00;        // clear the whole bit-field
   un_bit.a = 2;              // write into element a
   printf ("%d",un_bit.a);    // read from element a.
   return 0;
}

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

Не требуется для бит-полей

  1. Массивы бит-полей, указатели на бит-поля и функции, возвращающие бит-поля, недопустимы.
  2. Оператор адреса (&) не может применяться к элементам битового поля.
  3. Тип данных битового поля должен быть достаточно широким, чтобы содержать размер поля.
  4. Оператор sizeof() не может быть применен к битовому полю.
  5. Невозможно создать typedef для отдельного битового поля (хотя вы, безусловно, можете создать typedef для структуры, содержащей бит-поля).
typedef struct mybitfield
{
    unsigned char c1 : 20;   /* incorrect, see point 3 */
    unsigned char c2 : 4;    /* correct */
    unsigned char c3 : 1;
    unsigned int x[10]: 5;   /* incorrect, see point 1 */
} A;

int SomeFunction(void)
{
    // Somewhere in the code
    A a = { … };
    printf("Address of a.c2 is %p\n", &a.c2);      /* incorrect, see point 2 */
    printf("Size of a.c2 is %zu\n", sizeof(a.c2)); /* incorrect, see point 4 */
}


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow