:::: MENU ::::

Битовые операции C

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

Для начала создадим макрос для объявления битовых массивов:

typedef uint8_t BARR_ELTYPE;
#define BARR_ELBITS 8
#define BARR_ARRAYSIZE(N) ((N + BARR_ELBITS - 1) / BARR_ELBITS)

Так, для того чтобы создать, массив из 14 бит:

BARR_ELBITS arr_bit(BARR_ARRAYSIZE(N))

14 бит — это 2 байта без 2 бит. Определение количества байт идет путем деления количества байт(N) на количество байт в одной переменной(BARR_ELBITS) и округления значения в большую сторону. Для округления используется формула (A + B — 1) / B. Данная формула верна если A и B не отрицательны и A + B не создает переполнения.

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

#define BARR_ELMUM(N) (N / BARR_ELBITS)
#define BARR_BITNUM(N) (N % BARR_ELBITS)
#define BARR_SET(barr, N) ( barr[BARR_ELMUM(N)] |= ( (BARR_ELTYPE)1 << BARR_BITNUM(N) ) )
#define BARR_CLEAR(barr, N) ( barr[BARR_ELMUM(N)] &= ( ~( (BARR_ELTYPE)1 << BARR_BITNUM(N) ) ) )
#define BARR_INV(barr, N) (barr[BARR_ELMUM(N)] ^= ( (BARR_ELTYPE)1 << BARR_BITNUM(N) ))
#define BARR_TEST(barr, N) (barr[BARR_ELMUM(N)] & ( (BARR_ELTYPE)1 << BARR_BITNUM(N) ))

BARR_ELMUM — используется для определения элемента(байта) в котором находится бит, путем деления без остатка.

BARR_BITNUM — используется для определения позиции бита в элементе(байте), путем деления и взятия остатка.

BARR_SET и BARR_CLEAR — соответственно устанавливают и сбрасывают нужный бит в битовом массиве. Для этого они используют битовое «ИЛИ» и «И» с 1 и 0 в нужном разряде соответственно.

BARR_INV — инвертирует нужный бит. Для этого используется битовое исключающее «ИЛИ» с 1.

BARR_TEST — проверяет установлен ли нужный бит в 1 или нет. Если установлен, то возвращает не 0 (именно не 0, а не 1).

Пример:

typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;

typedef uint8_t  BARR_ELTYPE;
#define BARR_ELBITS 8
#define BARR_ARRAYSIZE(N) ((N + BARR_ELBITS - 1) / BARR_ELBITS)

#define BARR_ELMUM(N) (N / BARR_ELBITS)
#define BARR_BITNUM(N) (N % BARR_ELBITS)
#define BARR_SET(barr, N) ( barr[BARR_ELMUM(N)] |= ( (BARR_ELTYPE)1 << BARR_BITNUM(N) ) )
#define BARR_CLEAR(barr, N) ( barr[BARR_ELMUM(N)] &= ( ~( (BARR_ELTYPE)1 << BARR_BITNUM(N) ) ) )
#define BARR_INV(barr, N) (barr[BARR_ELMUM(N)] ^= ( (BARR_ELTYPE)1 << BARR_BITNUM(N) ))
#define BARR_TEST(barr, N) (barr[BARR_ELMUM(N)] & ( (BARR_ELTYPE)1 << BARR_BITNUM(N) ))


BARR_ELTYPE arr_bit[BARR_ARRAYSIZE(16)];

void main(void)
{
    BARR_SET(arr_bit, 0);
    BARR_SET(arr_bit, 1);
    BARR_CLEAR(arr_bit, 4);
    BARR_CLEAR(arr_bit, 1);
    BARR_INV(arr_bit, 1);
    
    printf("%d \n", arr_bit[0]);    
    if(BARR_TEST(arr_bit, 0) != 0)
    {
        printf("%d \n", sizeof(arr_bit));
    }
}

 

 

Note: помнить

Таблица истиности ^

 x1 x2 res
1  1  0
1  0  1
0  1  1
0  0  0 

Тогда когда ^ с 1, то всегда происходит изменение состояния.
Если с 0, то нет изменения состояния.

 x = (x ^ (1<<n)) // изменение состояния n-го бита переменной x

Для определения члена массива и номера байта которого надо изменить состояние можно использовать такую конструкцию:

amt_net_temp = work_buf_trans / 8; // определили какой член массива
amt_net_temp_2 = work_buf_trans % 8; // определили какой бит

// меняем состояние
f_busy_buf[amt_net_temp] = (f_busy_buf[amt_net_temp] ^ (1<<amt_net_temp_2));