Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Аленский. лекции по проге.doc
Скачиваний:
19
Добавлен:
11.11.2018
Размер:
1.35 Mб
Скачать

Глава 4 простые типы данных § 1. Целый тип

1.1. Битовые операции

В языке С++ есть следующие битовые операции:

& — битовое и;

| — битовое или;

^ — исключающее или;

~ — дополнение;

>> сдвиг вправо на указанное количество разрядов (бит);

<< сдвиг влево на указанное количество разрядов (бит).

Первые три операции являются бинарными. Они работают с двоичным представлением двух операндов (констант, переменных или выражений). Они выполняются над каждой парой битов по следующим правилам:

& | ^

1 1 1 1 0

1 0 0 1 1

0 1 0 1 1

0 0 0 0 0

Упражнение 1. Определить результат:

short int k1=30,k2=-1707,r; r=k1 & k2;

cout<<endl<< k1<< “&”<<k2<<”=”<<r;

1) Представляем положительное число 30 в оперативной памяти. Для этого переводим его в двоичную систему счисления делением на 2 следующим образом: 30/2=15 (0 в остатке), 15/2=7 (1 в остатке), 7/2=3 (1), 3/2=1(1), 1/2=0(1). Записывая все остатки в обратном порядке, получим 3010 =111102. Представляем это число в двух байтах, так как оно объявлено как short int. Для этого дописываем слева необходимое количество нулей и получаем 0000000000011110.

2) Представляем отрицательное число –1707 в оперативной памяти в дополнительном коде. Для этого выполняем следующее:

а) так как делением на 2 перевод в двоичную систему счисления выполняется долго, сначала переведём число 1707 во вспомогательную шестнадцатеричную систему счисления делением на 16. 1707/16=106 (11 в остатке), 106/16=6 (10), 6/16=0(6). Записывая все остатки в шестнадцатеричной системе счисления в обратном порядке, получим 170710 =6AB16, так как 1010=A16, 1110=B16;

б) каждую шестнадцатеричную цифру полученного результата записываем в виде четырёх двоичных цифр (двоичной тетрады): 011010101011;

в) представляем это число в двух байтах, так как оно объявлено как short int. Для этого дописываем слева необходимое количество нулей: 0000011010101011. Получили представление положительного числа в памяти компьютера;

г) для получения дополнительного кода выполняем инверсию положительного представления с учётом “левых” нулей, т. е. нуль меняем на единицу, а единицу на нуль. К результату инверсии прибавляем единицу. Получим 1111100101010101.

3) К полученным таким образом двоичным кодам двух чисел применяем битовую операцию и (&). При этом единицы получаем только там, где были обе единицы, во всех остальных разрядах получатся нули: 00000000000010100.

4) Что это за число в десятичной системе счисления? Для ответа на вопрос разбиваем его на тетрады и получаем число в шестнадцатеричной системе счисления: 1416. Для перевода в десятичную систему счисления записываем его как сумму степеней числа 16, умноженную на соответствующую шестнадцатеричную цифру: 1416=1*16+4=2010. При этом действия выполняются в десятичной системе счисления.

Упражнение 2. Найти r2=30 | -1707, если, a) short r2, b) unsigned short r2.

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

a) Так как результат объявлен как знаковое число (по умолчанию signed) и в самом левом бите получилась единица, то это дополнительный код отрицательного числа. Для его получения выполняем в обратном порядке то, что делали в пункте 2) предыдущего упражнения: вычитаем единицу и выполняем инверсию. Получим 0000011010100001. Разбив на тетрады, получаем 6A116=6*162+10*16+1=1697. Ответ: –1697.

б) Так как при таком объявлении (unsigned) отрицательного числа быть не может, то мы имеем положительное число, несмотря на единицу в самом левом бите. Разбиваем двоичное число на тетрады и переводим его из шестнадцатеричной системы счисления в десятичную: F95F16=15*163+9*162+5*16+15=63839.

Упражнение 3. Найти r3=30 ^ –1707, если: a) short r3, b) unsigned short r3.

Упражнение 4. Пусть short r3. Определить результат: r3=~30.

Операция дополнение (~) меняет единицу на нуль, а нуль на единицу. Поэтому к полученному в упр. 1 двоичному коду числа 30 0000000000011110 применяем инверсию: 1111111111100001. Так как по умолчанию переменная имеет модификатор signed и в самом левом бите получена единица, то это отрицательное число. Для его восстановления вычитаем единицу и выполняем инверсию: 0000000000011111=1F16=1*16+15=31. Ответ: –31.

Операция << (сдвиг влево) имеет общий вид a<<m, где a — число, участвующее в сдвиге, а m показывает, на какое количество разрядов сдвигаем число. При этом как для положительных, так и для отрицательных чисел “вращение”, или циклический сдвиг, не выполняется. Значения левых сдвинутых бит теряются, а справа появляются нули.

Например, в результате выполнения следующего фрагмента программы

char k= –5;

for(int I=1; I<=4; I++)

{ k<<=1; // или k=k<<1;

printf("%5d",k);

}

получим –10, –20, –40, –80. Почему? Число –5 имеет следующее представление в одном байте (тип char): 11111011. После сдвига на один разряд имеем 11110110. При этом левая крайняя единица потерялась, и справа появилось число 0, а не 1. Так как это отрицательное число, то после вычитания единицы и инверсии получаем 00001010, т. е. число 10. Поэтому первым будет выведено число –10. Аналогично получаем остальные числа.

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

Сдвиг вправо (a >> m) выполняется аналогично, без циклического сдвига. При сдвиге положительных чисел слева появляется m нулей, а при сдвиге отрицательных чисел слева добавляется m единиц.

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

Например, 42>>2 даёт 1010. Действительно, 42=001010102, а в результате сдвига получаем 000010102=1010. В результате операции –42>>2 получим –11. Почему? –42=110101102, а после сдвига получим 111101012, то есть отрицательное число –11.

В качестве упражнения выполните следующий фрагмент программы и объясните результат:

char k= –30; cout<<endl;

for(int I=1; I<=4; I++)

{ k>>=1; // или k=k>>1;

printf ( "%5d" , k);

}