Поразрядный оператор исключающего ИЛИ




Оказывается, поразрядный оператор исключающего ИЛИ (^) обладает волшебными свойствами. Листинг 2.6 демонстрирует работу этого оператора. Как и раньше, скомпилируйте и запустите эту программу, По приглашению введите два числа, разделенных одним пробелом,

Листинг 2.6. TXOR.C (поразрядный оператор исключающего ИЛИ)

___________________________________________________________

1: #include <stdio.h>

2: #include "pbin.c"

3:

4: main()

5: {

6: unsigned v1, v2, v3;

8: printf(“Enter values to OR exclusively: (ex: 1234 15) ");

9: scanf("%u %u", &v1, &v2);

10: v3 = v1 ^ v2;

11: printf(" %5u %#06x ", v1, v1); pbin(v1);

12: printf(“XOR %5u %# 6x, v2, v2); pbin(v2);

13: prlntf("====================================\n");

14: printf(" %5u %#06x ", v3, v3); pbin(v3);

15: return 0;

16 }

_______________________________________________________

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

Таблица 2.6. Поразрядная операция исключающего ИЛИ

А   ^ В   ==   С  
  ^   ==    
  ^   ==    
  ^   ==    
  ^   ==    

 

С помощью простого эксперимента можно продемонстрировать важное свойство оператора поразрядного исключающего ИЛИ. Запустите программу TXOR и введите числа 45000 и -1 (разделенные одним пробелом). Программа отобразит следующее:

45000 0xafc8 1010111111001000

XOR 65535 0xffff 1111111111111111

=================================

20535 0х5037 0101000000110111

Число -1 в шестнадцатеричном представлении будет выглядеть как 0xffff, т.е. как 16 -битовое беззнаковое значение, у которого все разряды равны 1. Если при выполнении исключающего ИЛИ это число взять в качестве маски, то с операндом, равным 45000, в результате получим 20535. В двоичном представлении все нули операнда превратятся в единицы. Поскольку все биты второго операнда, или маски, равны 1, операция поразрядного исключающего ИЛИ эффективно переключает первоначальные значения всех разрядов первого операнда на противоположные. Теперь будет интересно повторить эксперимент с тойже самой маской, новзяв в качестве первого операнда результат предыдущего теста. Запустим снова TXOR и введем числа 2053 и -1 (разделенные одним пробелом). На этот раз программа отобразит следующее:

20535 0х5037 0101000000110111

XOR 65535 0xffff 1111111111111111

====================================

45000 0xafc8 1010111111001000

Применение маски, равной -1, к результату предыдущего эксперимента восстановило первоначальный операнд 45000. Этот факт справедлив для единичных разрядов любой маски, а не только равной -1. Выполнение операторов

С = А ^ В;

D = С ^ В;

установит переменную D равной первоначальному значению А.

Это свойство часто приносит пользу в мультипликации, гдe образы хранятся в памяти в виде битовых шаблонов (называемых битовыми образами). Отображение образа с помощью операции исключающего ИЛИ над его разрядами с битовым образом фона и использование аналогичной операции с теми же разрядами снова восстановит первоначальный образ. Это дает визуальный эффект независимого движения одного образа поверх другого.

Рис. 2.3. Результат операции исключающего ИЛИ над операндом и маской

 

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

Сдвиг битов влево

Используйте оператор сдвига влево (<<) для выполнения сдвига битов влево на нуль или другое число позиций. Оператор

С = А << 3;

присваивает переменной С значение переменной А, сдвинутое на три бита влево.

Листинг 2.7 отображает результат поразрядного сдвига влево, примененного к двум 16 -битовым беззнако­вым значениям. Скомпилируйте и запустите эту программу, затем введите два числа - 89 и 3 (разделенные пробелом), и вы увидите результат сдвига числа 89 на 3 бита влево.

Листинг 2.7. TLEFT.C (поразрядный сдвиг влево)

1: #include <stdio.h>

2: #include "pbin.c"

3:

4: main()

5: {

6: unsigned v1, v2, v3;

7:

8: printf("Enter values to SHIFT LEFT: (ex: 1234 3) ");

9: scanf("%u %u", &v1, &v2);

10: v3 = v1 << v2;

11: printf(" %5u %#06x ", v1, v1); pbin(v1);

12: printf("<< %5u %#06x ", v2, v2); pbin(v2);

13: printf("===========================^========\n");

14: printf(" %5u %#06x ", v3, v3); pbin(v3);

15: return 0;

16: }

______________________________________________

В двоичном представлении сдвиг битов на одну позицию влево эквивалентен умножению исходного значения на 2, т.е. на основание двоичной системы счисления. То же самое справедливо и для значений, записанных в других системах счисления. Например, сдвиг цифр в десятичном числе 1234 влево на одну позицию и внесение нуля справа даст в результате число 12340, которое в 10 раз больше исходного числа по основанию 10. Но в язы­ке С все сдвиги выполняются в двоичном формате.

Поскольку двоичные компьютеры выполняют сдвиг влево очень быстро, то операции поразрядного сдвига влево являются быстрым способом умножения чисел на степень числа два. Убедитесь в этом, используя про­грамму TLEFT. Запустите программу, введите через пробел числа 4 и 1, и вы должны увидеть следующее:

 

4 0х0004 0000000000000100

<< 1 0х0001 0000000000000001

8 0х0008 0000000000001000

Сдвиг числа 4 влево на один разряд дает в результате 8, что, конечно же, равно произведению 4 на 2.

Рис.2.4 иллюстрирует действие оператора С = А << В;, где А равно двоичному значению 01011001, а В равно 3. Как видно по результату (С), освобождающиеся справа позиции заполняются нулями, в то время как двигаемые влево старшие биты теряются (что иллюстрируется на рисунке символом электрического заземления), а сами разряды "исчезают в земле" подобно электронам.


Рис. 2.4. Действие оператора поразрядного сдвига влево

 

Сдвиг битов вправо

Как вы могли догадаться, опера­тор поразрядного сдвига вправо ра­ботает аналогично оператору сдвига влево, но сдвигает биты в другом направлении. Оператор

С = А >> 3;

присваивает переменной С значение переменной А, сдвинутое на три би­та вправо.

Листинг 2.8 отображает результат оператора поразрядного сдвига вправо, примененного к двум 16 -битовым беззнаковым значениям. Скомпили­руйте и запустите эту программу, затем введите два числа 89 и 3 (разделенные пробелом), и вы увидите резуль­тат сдвига числа - 89 на 3 бита вправо.

Листинг 2.8. TRIGHT.C (поразрядный сдвиг вправо)

______________________________________________

1: #include <stdio.h>

2: #include "pbin.c"

3:

4: main()

5: {

6: unsigned v1, v2, v3;

7:

8: printf("Enter values to SHIFT RIGHT: (ex: 1234 3) ");

9: scanf("%u %u", &v1, &v2);

10: v3 = v1 >> v2;

11: printf(" %5u %#06x ", v1, v1); pbin(v1);

12: printf(">> %5u %#06x ", v2, v2); pbin(v2);

13: printf("====================================\n");

14: printf(" %5u %#06x ",v3, v3); pbin(v3);

15: return 0;

16: }

_______________________________________________________

Если операции сдвига влево умножают числа на 2, то операции сдвига вправо выполняют деление чисел. Аналогичное утверждение справедливо для основания любой системы счисления. Сдвиг числа 12340 вправо на одну цифру и отбрасывание нуля даст число 1234, которое мы получили бы при делении исходного числа на основание 10. Но в языке С все сдвиги выполняются в двоичном формате.

Чтобы увидеть, как сдвиг двоичных чисел вправо выполняет деление на 2, запустите программу TRIGHT, введите 89 и 3 и рассмотрите результат работы программы:

 

 

89 0х0059 0000000001011001

>> 3 0х0003 0000000000000011

==========================

X000b 0000000000001011

По законам целочислен­ной арифметики деление числа 89 на 2 в степени 3 (т.е. 8) даст в результате 11.

Рис. 2.5 иллюстрирует действие оператора

С = А >> В;

для тех же значений, кото­рые использовались в приме­ре на рис. 2.4.

Рис. 2.5. Действие оператора поразрядного сдвига вправо

Дополнение до единицы

Оператор поразрядного дополнения до единицы, представленный символом ~ (в математике тильда - сим­вол разности), переключает все биты значения с 1 на 0 и с 0 на 1. Дополнение до единицы является унарным оператором, который предшествует своему операнду подобно унарному плюсу или минусу (как в выражениях -34 или +value).

Используйте унарную операцию дополнения до единицы для выполнения поразрядного отрицания двоичных значений. Например, выражение

А = ~ В

присваивает переменной А значение дополнения или отрицания переменной В.

Листинг 2.9 демонстрирует оператор поразрядного дополнения до единицы. Введите и запустите програм­му, затем введите одно число, чтобы получить значение его дополнения.

Листинг 2.9. ТСОМР.С (поразрядное дополнение до единицы)

_____________________________________________________

1: #include <stdio.h>

2: #include "pbin.c"

3:

4: main()

5: {

6: unsigned int v1, v2;

7:

8: printf("Enter value to complement: ");

9: scanf("%u", &v1);

10: v2 = ~ v1;

11: printf(" %5d %#06x ", v1, v1); pbin(v1);

12: printf(“====================================\n");

13: printf("COMP %5d %#06x ", v2, v2); pbin(v2);

14: return 0;

15: }

_______________________________________________________

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

Если 0 представляет "ложь", то -0 является "истиной", так как отрицание нуля дает -1 (беззнаковое число 65535), а любое ненулевое число означает "истину". (Чтобы убедиться в этом, запустите ТСОМР и введите 0.) Обратное утверждение также справедливо: отрицание -1 равно 0. Однако отрицание не каждого ненулевого чис­ла дает 0.

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

-49 0xffd3 1111111111010011

=========================

СОМР 44 0х002с 0000000000101100

Очевидно, что 44+1 равно 45 - абсолютному значению числа -45 (беззнаковый эквивалент). Затем вве­дите число 45 и получите другой результат:

X002d 0000000000101101

=================================

СОМР -46 0xffd2 1111111111010010

Прибавление 1 к числу -46 дает первоначальное значение –45.

Один плюс дополнение до единицы любого знакового целого v равно тому же самому значению с противоположным знаком.

Рис. 6.6 иллюстрирует действие опера­тора дополнения до единицы, примененно­го к двоичному значению 00101100. Срав­ните биты 2-5 на этом рисунке с приме­ром исключающего ИЛИ на рис. 2.3. Вы­полнение исключающего ИЛИ над некото­рым битом и единичным битом пере­ключает значение первого бита (то же самое делает операция дополнения до единицы); эту важную связь следует запомнить. Например, операция ис­ключающего ИЛИ с операндами 00101100 и 11111111 даст тот же результат, что и выражение - 00101100.

Операторы присваивания

Вы уже познакомились с оператором присваивания, а теперь, так как вы такие хорошие друзья, пора взглянуть поближе на важный символ, представленный одним знаком равенства (=). Рассмотрим следующий простой оператор, который присваивает значение В переменной А:

А = В; /* Присвоить значение В переменной А */

В результате выполнения этого оператора значение В не изменяется, а прежнее значение А исчезает, как туман в лучах солнца.

Строго говоря, А называется l - значением (lvalue), а В - г -значением (rvalue). В общем случае l - значение - это любое выражение, которое может стоять слева от знака равенства. В свою очередь г - значение — это любое выражение, которое может стоять справа от знака равенства. l - значение (в нашем случае А) должно ссылаться на объект, который может принимать значения, - обычно это ссылка на некоторую область памяти; г - значение представляет собой любое выражение, имеющее значение, но оно не обязано хранить его — это может быть и ссылка и литеральная константа. Выражение

А = 1234

вполне законно при условии, что А может хранить числовые значения. Выражение

1234 = А; /*??? */

не имеет смысла (и не будет компилироваться), потому что литеральная константа (1234) не является l - значением.

Выражения значений

Выражения бывают разные - от очень простых до невероятно сложных. Простейшее выражение представ­ляет собой значение. Например, переменная А типа int является выражением, которое представляет значение переменной А. В действительности, все выражения имеют значения. Выражение (А + В) равно значению сум­мы слагаемых А и В.

Несмотря на то, что следующее утверждение может показаться странным, но, тем не менее, присваивания также являются выражениями и, подобно всем выражениям, имеют значения. Как вы уже знаете, выражение (А = В) присваивает значение В переменной А. Однако как выражение (А = В) также имеет значение, равное присваиваемому результату. Следовательно, выражение

С = (А = В)

полностью допустимо. Сначала заключенное в круглые скобки подвыражение (А = В) присваивает значение В переменной А. Затем значение этого подвыражения (равное А) присваивается переменной С. Таким образом, как С, так и А сейчас равны В. Можно даже обойтись без круглых скобок. Выражение

С = А = В

присваивает значение В переменным А и С.

Принимая это во внимание, возьмите на вооружение один из способов инициализации многих переменных. Выражение

С = А = В = 451

присваивает число 451 переменным А, В и С.



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-08-20 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: