Pers.narod.ru. Алгоритмы. Работа с отдельными битами на Си |
Вопрос:
Добрый вечер, подскажите как изменить бит в байте?
Мой ответ:
Побитовым исключающим или (операция ^) со степенью двойки от номера нужного бита (справа налево, младший бит имеет номер 0)
Пример на консольном Си:
#include <stdio.h>
#include <math.h>
void main () {
char b=0xFF; //Байт
int n=3; //Номер бита, который нужно изменить - считаем справа налево с 0
printf ("\nБыло: %02X",b);
b^=(int)pow(2,n); //Переключение нужного бита - 0 в 1 или 1 в 0
printf ("\nПосле переключения: %02X",b);
b^=(int)pow(2,n);
printf ("\nПереключили еще раз: %02X",b);
}
На самом деле вычислять степени двойки глупо, раз есть побитовый сдвиг, так что окончательно получаем такую версию:
#include <stdio.h>
void main () {
char b=0xFF; //Байт
int n=3; //Номер бита, который нужно изменить - считаем справа налево с 0
printf ("\nБыло: %02X",b);
b^=1<<n; //Переключение нужного бита - 0 в 1 или 1 в 0
printf ("\nПосле переключения: %02X",b);
b^=1<<n;
printf ("\nПереключили еще раз: %02X",b);
}
Если нужно просто проверить, установлен ли в байте тот или иной бит, в тех же обозначениях получится следующий код:
if (b&1<<n) {
//Бит с номером n установлен
}
else {
//Бит не установлен
}
Здесь нам помогла операция побитового "и" (бинарная операция &)
Наконец, бывает нужно принудительно включить или выключить некоторый бит независимо от его начального состояния. Для принудительного включения пригодится операция побитового "или" (|), ведь обычное "или" с единицей всегда даёт единицу, а с нулём оставляет состояние бита неизменным:
b|=1<<n; //Принудительно включаем бит номер n
Для принудительного выключения бита применим к байту побитовое "и" с числом, обратным к нужной степени двойки, то есть, таким, где все единичные биты заменены нулевыми и наоборот. Взять такое число можно побитовым отрицанием (операция ~), в итоге получится вот что:
b&=~(1<<n); //Принудительно выключаем бит номер n
Побитовое "и" с единицей оставит неизменными все биты кроме того, который сброшен нами в ноль.
Совместная работа с парой битовых флагов |
Можно ли на С++ так же компактно работать с парой битовых флагов? По-моему, все нужные проверки содержатся в приложенной консольной программке:
#include <stdio.h>
#define A 0x01
#define B 0x02
//Пусть младший бит - флаг A, второй бит - флаг B
void main () {
int x=0;
x|=A|B; //установка флагов A и B, x=3
printf ("\nx=%02X",x); //для вывода текущего x
x&=~(A|B); //сброс флагов A и B, x=0
printf ("\nx=%02X",x);
x=1;
if (x&A) //проверка флага A
printf ("\nфлаг A установлен");
x=3;
if ( (x&(A|B))==(A|B) ) //проверка, что установлены оба флага
//все скобки нужны - приоритеты!
printf ("\nустановлены оба флага");
x=0;
if ( !(x&(A|B)) ) //проверка, что сброшены оба флага
//все скобки нужны - приоритеты!
printf ("\nсброшены оба флага");
x=2;
if ( x&(A|B) ) //проверка, что установлен хотя бы 1 из флагов
printf ("\nустановлен хотя бы 1 из флагов");
x=1;
if ( (x&(A|B))==A ) //проверка, что установлен только флаг A
//все скобки нужны - приоритеты!
printf ("\nустановлен только флаг A");
int y=0;
int diff= x^y; //проверка, какими флагами отличаются x и y
if ( !(diff&A) ) printf ("\nФлаг А совпадает");
if ( !(diff&B) ) printf ("\nФлаг B совпадает");
getchar();
}
Как сделать, чтобы инвертировались (нули заменялись на единицы и наоборот) ровно N битов, начиная с позиции P, а все другие биты остались без изменений?
А вот:
#include <stdio.h>
void main () {
unsigned int x=0x38, mask, N=3, P=3;
//00111000 превратит в 0
printf ("\n%02X",x);
mask = ~(~0 << N) << P;
x = (x & ~mask) | (~x & mask);
printf ("\n%02X",x);
getchar();
}
Как так вышло:
~0 = 11111....11111 ~0 << N = 11111....11000 /* N нулей */ ~(~0 << N) = 00000....00111 /* N единиц */ ~(~0 << N) << P = 0...01110...00 /* N единиц на местах P+N-1..P */
|
|