- •Часть 2
- •18 Сентября 2012 г., протокол № 1
- •Предисловие
- •Глава 6 простые типы данных § 1. Целый тип
- •1.1. Битовые операции
- •Использование битовых операций
- •1.3. Упаковка и распаковка информации
- •§ 2. Логический тип
- •§ 3. Символьный тип
- •§ 4. Вещественный тип
- •§ 5. Преобразование типов
- •Преобразование типов в выражениях
- •Преобразование типов при присваивании
- •Г л а в а 7 введение в указатели
- •§ 1. Понятие указателя. Операции разыменования и разадресации
- •§ 2. Инициализация и присваивание указателей
- •§ 3. Распределение динамической памяти
- •Операция new
- •Операция delete
- •§ 4. Параметры-указатели. Функция ввода scanf
- •Упражнения, тесты
- •Г л а в а 8 одномерные массивы, указатели и функции
- •§ 1. Связь указателей и одномерных массивов. Передача массива в качестве параметра функции
- •§ 2. Сортировка одномерных массивов
- •§ 3. Сортировка массива по параметру числа
- •§ 4. Сортировка массива выбором.
- •§ 5. Сортировка массива вставками
- •§ 6. Динамические одномерные массивы
- •6.1. Порядок работы с динамическим массивом
- •6.2. Работа с динамическим массивом в классе. Деструктор
- •§ 7. Введение в строки
- •Глава 9 мАтрицы
- •§ 1. Объявление, способы определения матриц
- •§ 2. Вывод матриц
- •§ 3. Типы алгоритмов
- •3.1. Построчная обработка
- •Обработка матрицы по столбцам
- •3.3. Обработка всей матрицы
- •3.4. Обработка части матрицы
- •Преобразование матрицы
- •Построение матриц
- •§ 4. Матрицы, указатели и функции
- •Упражнения и тесты
- •Обработка матрицы по столбцам.
- •Список реКоМендуемой литературы
- •Сборники задач по программированию
- •Оглавление
- •Методы программирования:
- •Лекции, примеры, тесты
- •Пособие для студентов механико-математического факультета
- •В двух частях
- •Часть 2
Глава 6 простые типы данных § 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.1) так как делением на 2 перевод в двоичную систему счисления выполняется долго, сначала переведём число 1707 во вспомогательную шестнадцатеричную систему счисления делением на 16. 1707/16=106 (11 в остатке), 106/16=6 (10), 6/16=0(6). Записывая все остатки в шестнадцатеричной системе счисления в обратном порядке, получим 6AB16, так как 1010=A16, 1110=B16;
2.2) каждую шестнадцатеричную цифру полученного результата записываем в виде четырёх двоичных цифр (двоичной тетрады): 011010101011;
2.3) представляем это число в двух байтах, так как оно объявлено как short int. Для этого дописываем слева необходимое количество нулей: 0000011010101011. Получили представление положительного числа в памяти компьютера;
2.4) для получения дополнительного кода выполняем инверсию положительного представления с учётом “левых” нулей, т. е. нуль меняем на единицу, единицу на нуль. К результату инверсии прибавляем единицу. Получим 1111100101010101.
3. К полученным таким образом двоичным кодам двух чисел применяем битовую операцию и (&). При этом единицы получаем только там, где были обе единицы, во всех остальных разрядах получатся нули: 0000000000010100.
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.
b) Так как при таком объявлении (unsigned) отрицательного числа быть не может, то мы имеем положительное число, несмотря на единицу в самом левом бите. Разбиваем двоичное число на тетрады и переводим его в десятичную систему счисления F95F16=15*163+9*162+5*16+15=63839.
Пример 3. Пусть short r3. Определить результат: r3=~30.
Операция дополнение (~) меняет единицу на нуль, а нуль на единицу. Поэтому к полученному в упражнении 1 двоичному коду числа 30 0000000000011110 применяем инверсию: 1111111111100001. Так как по умолчанию переменная имеет модификатор signed и в самом левом бите получена единица, то это отрицательное число. Для его восстановления вычитаем единицу и выполняем инверсию: 0000000000011111=1F16=1*16+15=31.
Операция << (сдвиг влево) имеет общий вид a<<m, где a — число, участвующее в сдвиге, а m показывает, на какое количество разрядов сдвигаем число. При этом как для положительных, так и для отрицательных чисел “вращение”, или циклический сдвиг, не выполняется. Значения левых сдвинутых бит теряются, а справа появляются нули.
Пример 4. 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. Аналогично получаем остальные числа.
Сдвиг влево на один разряд означает увеличение целого числа в два раза, сдвиг влево на два разряда увеличивает число в четыре раза и так далее, сдвиг влево на m разрядов равносилен увеличению числа в 2m раза. Это не зависит от того, какое число, положительное или отрицательное, участвовало в сдвиге.
Сдвиг вправо (a >> m) выполняется аналогично, без циклического сдвига. При сдвиге положительных чисел слева появляется m нулей, а при сдвиге отрицательных чисел слева добавляется m единиц.
Сдвиг вправо на один разряд означает целочисленное деление на 2, сдвиг вправо на два разряда означает целочисленное деление на 4 и так далее, сдвиг вправо на m разрядов равносилен целочисленному делению числа на 2m
Пример 5. a) 42>>2 даёт 1010. Действительно, 42=001010102, а в результате сдвига получаем 000010102=1010.
b) В результате операции –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); }
