Warning: Cannot use a scalar value as an array in /home/admin/public_html/forum/include/fm.class.php on line 757

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/include/fm.class.php on line 770
Форумы портала PHP.SU :: Версия для печати :: Приложение к уроку № 1 - работа с битами
Форумы портала PHP.SU » PHP » Уроки php » Приложение к уроку № 1 - работа с битами

Страниц (1): [1]
 

1. OrmaJever - 18 Марта, 2012 - 19:47:52 - перейти к сообщению
PHP как и многие другие языки поддерживает побитовые операции, их можно посмотреть тут.
Но просто посмотреть не достаточно. Если вы до этого не работали с ними (в других языках) то понять их будет очень сложно, в этой теме я постараюсь объяснить что они из себя представляют и как ими пользоватся.
Начём с того что в компьютерном мире любой символ представлен в виде последовательности битов (8 бит это 1 байт). Биты могут состоять только из 0 или 1. В 32 битных системах тип int занимает 4 байта в памяти (32/8) и может хранить число от -2147483648 до 2147483647. Откуда берутся эти ограничения тоже постараюсь обьяснить в этой теме.
Для того что бы всё понять я напишу биты чисел от 0 до 20, далее на них и будут показаны все примеры. Поскольку будем брать числа только до 20 то я возьму 8 бит (больше нету смысла там будут одни нули).
CODE (text):
скопировать код в буфер обмена
  1. 0000 0000 - 0
  2. 0000 0001 - 1
  3. 0000 0010 - 2
  4. 0000 0011 - 3
  5. 0000 0100 - 4
  6. 0000 0101 - 5
  7. 0000 0110 - 6
  8. 0000 0111 - 7
  9. 0000 1000 - 8
  10. 0000 1001 - 9
  11. 0000 1010 - 10
  12. 0000 1011 - 11
  13. 0000 1100 - 12
  14. 0000 1101 - 13
  15. 0000 1110 - 14
  16. 0000 1111 - 15
  17. 0001 0000 - 16
  18. 0001 0001 - 17
  19. 0001 0010 - 18
  20. 0001 0011 - 19
  21. 0001 0100 - 20
  22.  

Как Вы можите заметить, каждое слещующее число в битовом представлении это обычное число но состоящее только из 0 и 1, тоесть 1, после еденицы из нулей и едениц идёт 10, после десяти 11, затем 100, 101, и т.д.
Примечание: всё побитовые операторы возвращают число.

1) $a & $b - Побитовое 'и'. Устанавливаются только те биты, которые установлены и в $a, и в $b.
Для примера возьмём число 7 и 14.
PHP:
скопировать код в буфер обмена
  1.  
  2. echo 7 & 14; // результат 6.
  3.  

Но почем имено 6? Вот сейчас и разберёмся. Берём биты числа 7 и 14, и как написано в описанни оператора устанавливаем те биты которые есть и у 7 и у 14.
0000 0111 - 7
0000 1110 - 14
0000 0110 - и эти самые биты которые совпали, это и есть биты числа 6.
Ещё пример:
PHP:
скопировать код в буфер обмена
  1.  
  2. echo 10 & 15; // результат 10.
  3.  

0000 1010 - 10
0000 1111 - 15
0000 1010 - 10
Где может быть полезен этот оператор? Результат не может быть больше меньшего числа, тоесть если 10 & (любое число), то результат в любом случае не будет больше 10, это можно применить например если у нас есть масив из 10 символов и пользователь может выбрать одно из них, но он же может ввести 11 например и будет ошибка, а если использовать этот оператор то за пределы масива мы точно не выйдем.
PHP:
скопировать код в буфер обмена
  1.  
  2. $arr = array(1,2,3,4,5,6,7,8,9,0);
  3. echo $arr[9 & $_GET['key']]; // в $_GET['key'] может быть любое число, даже выходящее за пределы масива.
  4.  


2) $a | $b - Побитовое 'или'. Устанавливаются те биты, которые установлены либо в $a, либо в $b.
PHP:
скопировать код в буфер обмена
  1.  
  2. echo 7 | 14; // результат 15.
  3.  

Снова берём биты 7 и 14, но теперь устанавливаем те биты которые есть у 7 либо у 14. Тоесть просто берём биты 7 и по верху накладываем биты 14.
0000 0111 - 7
0000 1110 - 14
0000 1111 - 15
Ещё пример
PHP:
скопировать код в буфер обмена
  1.  
  2. echo 5 | 8; // результат 13.
  3.  

0000 0101 - 5
0000 1000 - 8
0000 1101 - 13
Где может пригодится этот оператор? Ну самый лучший пример это уровни ошибок в PHP (которые устанавливатся функцией error_reporting()), константы ошибок (E_NOTICE, E_WARNING, и т.д) содержат в себе число, которое имеет только 1 бит (1,2,4,8,16, и т.д) и когда вы совмещаете эти биты каждый бит занимает свою позицию и они не мешают друг другу.
0000 0001 - 1
0000 0010 - 2
0000 0100 - 4
0000 1000 - 8
0001 0000 - 16
А если совместить все то будет
0001 1111
и затем их можно просто разделить и узнать значения. Это полезно тем что в 32 битном числе можно хранить 32 булевых значения.

3) $a ^ $b - Исключающее 'или'. Устанавливаются только те биты, которые установлены либо только в $a, либо только в $b
Я думаю вы уже поняли как работать с битами поэтому просто пару примеров.
PHP:
скопировать код в буфер обмена
  1.  
  2. echo 7 ^ 14; // результат 9.
  3.  

00000111 - 7
00001110 - 14
00001001 - 9
PHP:
скопировать код в буфер обмена
  1.  
  2. echo 7 ^ 11; // результат 12.
  3.  

00000111 - 7
00001011 - 11
00001100 - 12
Где может быть полезен этот оператор? Ну вернёмся к тем же ошибкам в php, если мы хотим включить все ошибки кроме E_NOTICE, нам не нужно перечислять все 11 констант, а достаточно просто исключить из E_ALL ^ E_NOTICE.

4) ~ $a - Отрицание. Устанавливаются те биты, которые в $a не установлены, и наоборот.
Этот оператор можно сказать выворачивает биты на изнанку.
PHP:
скопировать код в буфер обмена
  1.  
  2. echo ~4; // результат будет немного неожиданым -5
  3.  

0000 0100 - 4
1111 1011 - -5

PHP:
скопировать код в буфер обмена
  1.  
  2. echo ~13; // результат -14
  3.  

0000 1101 - 13
1111 0010 - -14

немогу прям так сказать где можно применить этот оператор, но иногда он нужен. Если заинтересуетесь побитовыми операциями то найдёте ему применение.
5) $a << $b - Сдвиг влево - Все биты переменной $a сдвигаються на $b позиций влево (каждая позиция подразумевает 'умножение на 2')
Здесь всё просто, все биты сдвигаются влево на $b позиций, и $b позиций слева заполняются нулями.
PHP:
скопировать код в буфер обмена
  1.  
  2. echo 1 << 2; // результат 4
  3.  

0000 0001 - 1
0000 0100 - 4
PHP:
скопировать код в буфер обмена
  1.  
  2. echo 6 << 1; // результат 12
  3.  

0000 0110 - 6
0000 1100 - 12
Так совпало что каждый здвиг влево умножает число на 2.

Точно по такому же принципу работет и Сдвиг вправо.
$a >> $b - Сдвиг вправо. Все биты переменной $a сдвигаються на $b позиций вправо (каждая позиция подразумевает 'деление на 2')
PHP:
скопировать код в буфер обмена
  1.  
  2. echo 19 >> 1; // результат 9
  3.  

Сдвигает биты вправо на $b позиций и заполняет $b позиций слева нулями.
0001 0011 - 19
0000 1001 - 9
Куда можно применить эти 2 оператора? Они часто применятся на пару с "&" или "|", чуть ниже я покажу пример.

Ну вот мы немножко разобрались в побитовых опарациях и теперь что бы их закрепить надо что то написать.
Я решил показать как можно узнать биты любого числа с помощью побитовых операций. Начнём
Что бы узнать установлен ли бит можно пользоватся побитовым "и"
PHP:
скопировать код в буфер обмена
  1.  
  2. // 1 - установлено, 0 - нет.
  3. echo 3 & 1; // 1
  4. echo 4 & 1; // 0
  5.  

Почему имено 1 и 0? Снова обратимся к битам
0000 0001 - 1
0000 0011 - 3
0000 0001 - 1 // совпал 1 бит, и получилось число 1

0000 0001 - 1
0000 0100 - 4
0000 0000 - 0 // нет совпадений битов и поэтому 0
А как же нам теперь так проделать со всеми битами? Неужеле число надо поочереди сравнивать с 1,2,4,8,16, и т.д.? Нет, вот для этогои нужен здвиг.
Создадим цикл for, из 32 итераций (для 32 битного числа), и за каждую итерацию цикла мы будем проверять первый бит и здвигать на $i позиций вправо, а результат записывать в строку.
PHP:
скопировать код в буфер обмена
  1.  
  2. $num = 43;
  3. $str = '';
  4. for($i=0;$i<32;++$i) $str .= ($im >> $i) & 1;
  5. // и затем в конце нам нужно перевернуть строку, т.к. биты идут в обратно последовательности.
  6. echo strrev($str);
  7.  

А вот как будет проходить каждая итерация цикла. (я убрал 24 левых бита, там все равно нули)
[1 итерация] Сдвигает на 0 позиций и сравниваем биты с еденицей.
0010 1011 - 43
0010 1011 - 43
0000 0001 - 1
// разультат
0000 0001 - 1
[2 итерация] Сдвигает на 1 позицию и сравниваем биты с еденицей.
0010 1011 - 43
0001 0101 - 21
0000 0001 - 1
// разультат
0000 0001 - 1
[3 итерация] Сдвигает на 2 позиции и сравниваем биты с еденицей.
0010 1011 - 43
0000 1010 - 10
0000 0001 - 1
// разультат
0000 0000 - 0
и т.д.
Таким же способом можно работать с большими числами.
Надеюсь эта статья кому то помогла.
2. EuGen - 18 Марта, 2012 - 21:09:41 - перейти к сообщению
Разве же не
http://forum.php.su/topic.php?fo...m=62&topic=2 (второй комментарий, от Champion) ?

P.S. цыкл - некорректно

P.P.S предлагаю оформить как приложение, так как материал расширен и полезен, спасибо.
3. OrmaJever - 18 Марта, 2012 - 21:33:44 - перейти к сообщению
EuGen пишет:
P.S. цыкл - некорректно

Извиняюсь, безграмотный.
EuGen пишет:
Разве же не
http://forum.php.su/topic.php?fo...m=62&topic=2 (второй комментарий, от Champion) ?

Я искал только по названиям тем, думал этому будет отдельная тема.
4. Master_pascal - 31 Декабря, 2012 - 09:05:47 - перейти к сообщению
и в чем это применяется?

 

Powered by ExBB FM 1.0 RC1