Отправляет email-рассылки с помощью сервиса Sendsay

Инвестирование с нуля

  Все выпуски  

Программирование на Си и С++ с нуля 174) Побитовые операторы


Школа программирования

174) Программирование на С++: Побитовые операторы

Письмо.

Здравствуйте уважаемый Сергей Бобровский! Меня зовут Сергей. Программированием я занимаюсь 2,5 года, и два года я писал программы на Visual Basic, не решаясь взяться за другие языки программирования. Хочу поблагодарить вас за то, что я смог научиться программированию ещё на двух языках за 3 месяца по вашим рассылкам "Базовый курс" и "Программирование на С++ с нуля". Подобных рассылок в Интернете нет и это чистая, правда! Благодаря даже небольшому знанию C++ можно программировать 3D игры. Для этого даже движок писать не надо - в Интернете есть бесплатные исходники таких игр как Half-life (1). Изучив один из ваших последних уроков про наследование классов, я смог написать для этой игры новое оружие - нож сделав его из класса монтировки и всё почти с нуля! Рассказывайте, пожалуйста, побольше про классы - ведь на них держится всё современное игровое программирование вот реальный заголовок класса автомата из Half-life(думаю это покажется интересным):

  class CMP5 : public CBasePlayerWeapon //Обьявляем класс автомата
  из оружий игрока

  {

  public: //Глобальные

  void Spawn( void ); //Процедура загрузки на карту

  void Precache( void ); //Процедура прекиша (кеша перед загрузкой)

  int iItemSlot( void ) { return 3; } //Функция слота интеджер

  int GetItemInfo(ItemInfo *p); //Функция инфы об оружии интеджер

  int AddToPlayer( CBasePlayer *pPlayer ); //Функция добавления к
  игроку если взял true если нет false

  void PrimaryAttack( void );//Процедура первичной аттаки

  void SecondaryAttack( void ); //Процедура вторичной аттаки

  int SecondaryAmmoIndex( void ); //Функция значения вторичной
  амуниции

  BOOL Deploy( void );//Функция взятия в руки булеан

  void Reload( void );//Процедура перезарядки

  void WeaponIdle( void ); //Процедура бездействия

  float m_flNextAnimTime; //Функция флоат

  int m_iShell;

  private: //Локальные

  unsigned short m_usMP5;

  };

А ведь ещё месяц назад я думал, что этот код только для красоты. На тех же классах держится великая игра Half-life 2, это значит теперь я смогу редактировать такие продвинутые игры!
Мне хотелось бы спросить: что делают эти операторы:

  <<,>>, &=, ^=, |=,<<= ?

Их я нашёл в этих исходниках, но незнаю что они делают. Расскажите, пожалуйста, о них я думаю, может ещё кому-то понадобиться.
P.S. Хорошие курсы программирования легко отличить от плохих: если преподаватель говорит что язык сложный, вы тупой а он один умный - то это однозначно плохие курсы и вас там ничему не научат, а если говорить о пользе программирования, разговаривает с учениками, вдохновляет их и говорит, что ничего сложного в программировании нет - то это великий мастер и он научит вас всем премудростям написания программ.

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

Описание класса обычно отделяется от реализации входящих в него методов, чтобы не смешивать интерфейс и реализацию. Обычно структура класса выполняется в некоем стандартизованном виде - так, в данном примере видно, что каждая переменная для наглядности имеет свой префикс. Например, m_usMP5 - первая буква m означает member (член класса), далее следует упоминание ее типа (us - первые буквы unsigned short), и лишь потом название. Это древняя практика Microsoft, еще 10-15 летней давности, которой она рекомендовала придерживаться при использовании Microsoft Visual Studio, однако в последних версиях для .NET, если не ошибаюсь, от этой практики решено отойти.

Вообще, изучать исходные тексты профессиональных проектов очень полезно! Такой опыт весьма позитивен.

Побитовые операции

В языке С++ существуют так называемые побитовые операции - они выполняют некоторую операцию над парами битов двух операндов. Любое значение, как известно, представляется в компьютере в двоичном виде (в двоичной системе счисления, в отличие от общепринятой десятичной) - то есть записывается только с помощью двух цифр, нуля и единицы. Так, двойка запишется как 10, десятичное число 10 - как 1010.

Лирическое отступление про двоичные числа

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

1010 (двоичное) =1*2-в-кубе + 0*2-в-квадрате + 1*2-в-первой-степени + 0*2-в-нулевой-степени = 1*8 + 0*4 + 1*2 +0*1 = 10 десятичное

Но вдобавок к этому каждое значение хранится в памяти в отведенной ему ячейке, размер которой кратен одному байту (восьми двоичным разрядам или восьми битам). Минимальный размер ячейки - один байт. Современные 32-разрядные процессоры ориентированы на обработку, как явствует из их названия, на ячейки длиной 4 байта (4*8 битов = 32 разряда). Более современные 64-разрядные модели обрабатывают, соответственно, ячейки длиной 8 байтов.

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

То есть машинное слово 32-разрядного процессора - это ячейка размером 4 байта.

Первые микропроцессоры работали с восьмиразрядными словами, то есть с отдельными байтами. Язык Си был разработан как раз в те времена, поэтому, хотя он исходно ориентировался на разные аппаратные архитектуры и считался железо-независимым, исходно в него неформально была включена поддержка 8-разрядных слов в виде типа char.

Это двоичное восьмиразрядное число:
10010011

Это - тоже:
00000001

Языки Си/С++ допускают операции над отдельными двоичными разрядами чисел.

Например, есть два числа, представленые в двоичном виде: 00000001 и 00000010. Для них допустимы операции логического (побитового) сложения (символ | ), логического умножения (символ & ) и логического исключающего сложения (символ ^ ).

Операция | (ИЛИ) действует так - результатом будет 1, если хотя бы один аргумент равен 1.
Операция & (И) действует так - результатом будет 0, если хотя бы один аргумент равен 0.
Операция ^ выполняет так называемое исключающее ИЛИ (xor). Он выдает 1, если операнды не равны, и 0, если равны. Эта операция интересна тем, что, применив ее два раза к некоторому числу (точнее, сначала к исходному числу, а затем к его xor-версии) с заданным аргументом, мы получим исходное число. Поэтому операция XOR часто применяется в компьютерной графике - для быстрого перемещения спрайтов по фону (два раза накладываем маску спрайта на фон - и фон остается исходным), а также в криптографии для быстрого шифрования (взламывается, впрочем, это шифрование тоже очень быстро :-).

Тогда

   00000001 | 00000010 = 00000011
   00000001 & 00000010 = 00000000
   00000001 ^ 00000010 = 00000011 - применяем xor к числу 00000001 ...
   00000011 ^ 00000010 = 00000001 - теперь с тем же ключом 00000010 применяем xor к промежуточному результату, и получаем исходное число 00000001

Аргументы не обязательно записывать именно в двоичном формате (да и нету в С++, если не ошибаюсь, возможности записи чисел в двоичном виде? только в десятичном, шестнадцатеричном и восьмеричном) - компилятор все равно преобразует любые форматы во внутренний машинный вид (двоичный :-). Поэтому предыдущие примеры можно переписать так:

   1 | 2 = 3 (числа в десятичной системе счисления)
   1 & 2 = 0
   1 ^ 2 = 3
   3 ^ 2 = 1

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

Тип char традиционно определяет значения в диапазоне от -128 до +127. Это один байт. Существует также вариант беззнаковых (неотрицательных) целых - когда перед названием типа ставится ключевое слово unsigned (беззнаковое). Подобные числа считаются всегда положительными, и старший бит задействуется в качестве элемента значения, а не его знака.
Например, unsigned char задает диапазон от 0 до 255.

Битовые операции обычно проводят над беззнаковыми целыми, чтобы в результате не возникало путаницы со знаком. Например:

   unsigned char b1 = 1, b2 = 2, br;
   br = b1 | b2; // br равно 3

Существуют также операции побитового сдвига >> и << . Они сдвигают двоичную запись числа вправо или влево на заданное число разрядов (битов). Например:

   00000001 << 3 = 00001000 // сдвиг влево на три позиции
   00000010 >> 1 = 00000001 // сдвиг вправо на одну позицию

При этом новые, - появляющиеся, разряды всегда заполняются нулями, а исчезающие разряды никак не "компенсируются" в значении числа.

Сдвиг вправо на несколько разрядов можно, очевидно, использовать как деление на степени двойки.

В самом простом случае сдвиг числа вправо на один разряд можно считать делением на два:

    unsigned int N;
    N = 8;
    N = N >> 1; // поделили N на два

Умножать на два сдвигом влево тоже можно, но здесь существует опасность пропажи старших разрядов, которые "уезжают" влево за пределы числа, и значение искажается.

Такой прием деления можно довольно часто встретить в исходных текстах известных программ. Дело в том, что поразрядный сдвиг выполняется значительно быстрее, нежели операция деления, поэтому он часто задействуется в программах, где быстродействие и работа с отдельными битами весьма важны. Это, например, всевозможные системные утилиты, различные графические движки - немудрено, что там распространены побитовые операции!
А различные операции, побитовые И / ИЛИ, часто использовались, когда компьютеры были совсем маленькими (по своим ресурсам, обратно пропорциональным их размерам :),и объем оперативной памяти составлял десятки или сотни килобайтов. Тогда приходилось экономить даже не байты, а отдельные биты, хитроумными способами упаковывая информацию в отдельные разряды.
Но в рядовых прикладных задачах, где скорость непринципиальна (одна секунда или две, какая разница), да и объем оперативной памяти непринципиален (несколько десятков лишних мегабайтов ничего не изменят), побитовые операции сегодня практически не используются, потому что их результат не всегда предсказуем, да и код получается плохо наглядным.

Но знать побитовые операции надо :)

Существуют сокращенные записи всех упомянутых пяти операций, ориентированные на оператор присваивания. Как принято в Си, форму присваивания с операцией О :

переменная = переменная О значение;

можно переписать более компактно (при условии, что имя переменной одно и то же):

переменная О= значение;

То есть запись

   X = X | 10;

допустимо записать в сокращенном виде:

   X |= 10;

Это правило относится и к другим побитовым операциям:

   X = X & 10;

запишется как
   X &= 10;

   X = X ^ 10;

запишется как
   X ^= 10;

   X = X >> 2;

запишется как
   X >>= 2;

   X = X << 1;

запишется как
   X <<= 1;


(c) 2004-2007 Сергей Бобровский : bo собака russianenterprisesolutions.com

Школа программирования с нуля
Все предыдущие выпуски базового курса всегда тут:
http://www.infiltration.ru/p/

Неофициальный сайт поддержки (со срочными вопросами - сюда):
www.prog-begin.net.ru.


Мои книги (учебные курсы) "Технологии Delphi / C++ / C#. Разработка приложений для бизнеса".
http://shop.piter.com/display.phtml?a_id=17681&web_ok=all

Все эти учебные курсы рассчитаны не только на разработчиков, но и на всех тех, кто хочет стать ИТ-менеджером. Для этого как минимум нужно иметь общее представление о современных технологиях разработки и их истории и владеть соответствующей терминологией. Именно это мои книги и дают.
В учебных курсах описаны десятки технологий, каждой из которых посвящены отдельные книги. Таким образом, купив один учебный курс, вы существенно сэкономите :) В книгах полностью описаны:
- Delphi (версия 2006, полностью совместимая с Delphi 2005/2006/2007 и Turbo Delphi) для обеих платформ - Win32 и .NET;
- C# (новый язык Microsoft, на котором базируется платформа .NET и все новые версии Windows);
- C++ для платформы Win32.
Охвачены также темы работы с файлами на этих платформах, создания файл-серверных, клиент-серверных, распределенных приложений, веб-программ (Indy, ASP.NET, веб-сервисы). Описаны языки SQL и OCL. Немало глав посвящены истории программирования и различных технологий. Особое внимание уделено созданию программ с помощью технологии ECO и языка моделирования UML - программы фактически рисуются, и теперь даже для создания корпоративных приложений и их переноса в Интернет не обязательно знать программирование!
Отдельная часть отведена технологиям организации групповой работы, управления требованиями, контроля версий, локализации и тестирования.
Тут подробнее про книги.

Мои книги, которые пока доступны в продаже:


Дизайн рассылки: Алексей Голубев - Web-дизайн и web-программирование


В избранное