Пятница, 17.11.2017, 23:20
Приветствую Вас Гость | RSS

Кузница миров

Меню сайта
Категории раздела
Мои статьи [2]
Курс : "Основы С++ для начинающих программистов игр." [25]
WindMill Engine [3]
XNA4 RPG [0]
Перевод туториалов но созданию RPG на C# c XNA4.
C# & Leadwerks [5]
Программирование Leadwerks Engine на языке С# с помощью врапера Le.NET.

Каталог статей и уроков

Главная » Статьи » Курс : "Основы С++ для начинающих программистов игр."

4.5. Манипуляция с битами
4.5. Манипуляция с битами.


Думаю, раз биты такие важные, что с них мы начали наш урок, нужно познакомится с ними еще ближе. В языке С++ есть несколько операторов, с помощью которых можно проводить различные действия над битами:

~ Битовое Не. Меняет значение бита на противоположное. Вообще, если сравнить 1 с логическим true, а ноль с логическим false (как оно в общем-то и есть) здесь можно использовать аналогию с логическими операторами. Помните пример:

Code
!true возвратит false  !false возвратит true


Ну а вот битовый аналог:

Code
~1 (true) возвратит 0 (false)  ~0 (false) возвратит 1 (true)


& Битовое И. Как и логический вариант, этот оператор возвращает 1 если оба аргумента равны единице, во всех остальных случаях 0.

Code
1 (true)  & 1 (true)  возвратит 1 (true)  1 (true)  & 0 (false) возвратит 0 (false)  0 (false) & 0 (false) возвратит 0 (false)


Битовый & поочередно применяется ко всем битам числа. Вот как это работает:

11010 & 10101 равно 10000

Немного отформатируем, чтоб было понятнее:
11010 &
10101 равно
10000

Если перевести все в привычную нам десятичную систему, то будет так:

26 & 21 = 16

Вот такая вот арифметика)
Воплотим ее в код:

Code
#include <iostream>  using namespace std;    void main()  {       cout<< (26 & 21);       cin.get();  }  


| Битовое ИЛИ. И опять знакомая ситуация. Если один из аргументов равен 1, то 1 и будет возвращено .

Code
1 (true)  |  1 (true)  возвратит 1 (true)  1 (true)  |  0 (false) возвратит 1 (true)  0 (false) |  0 (false) возвратит 0 (false)


Пример:
11010 |
10101 равно
11111
26 | 21 = 31

Измените предыдущий пример и убедитесь, что я прав.

^ Исключающее ИЛИ. А вот это нечто новое. Такого мы еще не видели. Оператор ИЛИ возвращает единицу, только если один(только один) из аргументов равен 1.

Code
1 (true)  ^  1 (true)  возвратит 0 (false)  1 (true)  ^  0 (false) возвратит 1 (true)  0 (false) ^ 1 (true)   возвратит 1 (true)  0 (false) ^  0 (false) возвратит 0 (false)


Пример:
11010 ^
10101 равно
01111

В привычном нам, десятичном, виде:

26 ^ 21 = 15

Операторы сдвига


Помимо логических, для работы с битами также используются операторы сдвига. Это уже знакомые нам << и >>. Они сдвигают все биты числа соответственно влево или вправо, заполняя освободившиеся нолем.
Синтаксис использования этих операторов выглядит так:

переменная << число битов
переменная >> число битов


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

01011010 << 1 даст
10110100

а

01011010 >> 1 даст
00101101

Отлично, сдвигает. Нам то что с того.
Ну например использование комбинация битовых операторов позволяет узнавать или изменять значение каждого бита на свое усмотрение. Для этого придется немного напрячь мозг. Чтобы понять приведенные далее коротенькие примеры необходимо знать то, что я излагал выше.

Проверка бита

Итак, в первую очередь разберемся, как узнать значение конкретного бита переменной v.
Для этого используется вот такое очень простое выражение:

Code
(v & (1 <<n))? Бит = 1: Бит = 0;


Эта запись, если вы помните прошлые уроки, использует условный оператор и может быть записана следующим образом:

Code
if(v & (1 << n))  {       Бит = 1;  }  else  {       Бит = 0;  }


Где:
v - переменная,
n - номер бита.

Работает оно так. Сначала с помощью оператора сдвига создается переменная, в которой интересующий нас бит равен 1. Для этого переменная 1 имеющая двоичное представление 00000001, сдвигается на нужное количество битов влево. Допустим нам нужно узнать значение второго бита. Вспомним, что биты нумеруются от ноля.

(1 << 2)
00000001 << 2
Результат сдвига
00000100

Далее применяем битовый И. Если у переменной второй бит 1 то в результате выражения:

Code
    11111111  & 00000100  Получим    00000100  то есть (true)


Если же второй бит равен нолю то:

Code
    11111011  & 00000100  Получим         00000000  то есть (false)


Ну а дальше в действие вступает условный оператор, который определяет правдивое выражение или ложное и присваивает соответствующую цифру.
Далее я продемонстрирую как с помощью описанного выше способа выполнить преобразование из десятичной системы счисления в двоичную.

Code
#include <iostream>  using namespace std;    void main()  {       setlocale(0,"");       cout<<"введите число от 0 до 255 ";       int i;       cin>>i;       for(int n=7;n>=0;n--)       {        cout<<(i & (1 << n) ? 1 : 0);       }       cin.get();       cin.get();  }  


Намного проще, не правда ли. Если кому-то непонятно, замените строку:

Code
  cout<<(i & (1 << n) ? 1 : 0);


на:

Code
  if(i & (1 << n))        {         cout<<1;        }        else        {         cout<<0;        }


Теперь дошло?

Установка бита в 1

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

Code
v = (v | (1 << n))


Здесь, как и в прошлом случае используется сдвиг для получения переменной, в которой значение конкретного бита 1, остальных 0.
Далее используется битовый оператор ИЛИ. Если у переменной второй бит и так 1 то в результате выражения:

11111111 | 00000100 Получим 11111111

Если же второй бит равен нолю то:

000000000 | 00000100 Получим 00000100

Далее результат присваивается исходной переменной.

Пример кода:

Code
#include <iostream>  using namespace std;    void main()  {       setlocale(0,"");       int i=0;       cout<<"i = "<<i<<" = ";       for(int n=7;n>=0;n--)       {        cout<<(i & (1 << n) ? 1 : 0);       }         i |= (1 << 0);    // устанавливаем нулевой бит в 1       cout<<"\nустанавливаем нулевой бит в 1\n";       cout<<"i = "<<i<<" = ";       for(int n=7;n>=0;n--)       {        cout<<(i & (1 << n) ? 1 : 0);       }         i |= (1 << 7);    // устанавливаем 7 бит в 1       cout<<"\nустанавливаем 7 бит в 1\n";       cout<<"i = "<<i<<" = ";       for(int n=7;n>=0;n--)       {        cout<<(i & (1 << n) ? 1 : 0);       }         cin.get();       cin.get();  }


Здесь, вместо строки

Code
v = (v | (1 << n))


у меня записано

Code
i |= (1 << 0);


Для сокращения записи я воспользовался составным присваиванием |=.

Установка бита в 0

А теперь обнулим бит с помощью:

Code
v &= ~(1 << f)


(Здесь тоже составное присваивание &=)

И вновь используется сдвиг, для получения переменной, в которой значение конкретного бита 1, остальных 0. Далее с помощью битового НЕ значение всех битов меняется на противоположное.

~00000100 дает 11111011

Затем, как и в случае проверки, используется битовый И.
Если у переменной второй бит 1 то в результате выражения:

01010101 & 11111011 Получим 01010001

Если же второй бит уже равен нолю, то там и менять нечего:

10101010 & 11111011 Получим 10101010

Этот код нужно добавить в приведенный выше пример перед cin.get();.

Code
i &= ~(1 << 0);    // обнуляем нулевой бит       cout<<"\nобнуляем нулевой бит\n";       cout<<"i = "<<i<<" = ";       for(int n=7;n>=0;n--)       {        cout<<(i & (1 << n) ? 1 : 0);       }         i &= ~(1 << 7);    // обнуляем 7 бит       cout<<"\nобнуляем 7 бит\n";       cout<<"i = "<<i<<" = ";       for(int n=7;n>=0;n--)       {        cout<<(i & (1 << n) ? 1 : 0);       }


Вот мы и научились управлять данными на таком тонком уровне как биты. Для полного счастья нам не хватает только добраться до памяти, а конкретно до тех ячеек, в которых вместе со своими битами хранятся переменные. Чем-то подобным мы далее и займемся.
Категория: Курс : "Основы С++ для начинающих программистов игр." | Добавил: nilrem (11.05.2012)
Просмотров: 3513 | Рейтинг: 3.5/4
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0