Задачи и упражнения.
1. Пусть int a=170. Определить результат:
а) r = a & 15;
б) r = a | 8;
в) if (a & 8 = = 8) cout<<” Yes”; else cout<<”No”;
Решение. Число 170 переводим в шестнадцатеричную систему счисления делением на 16. Получим 17010=AA16. Каждую шестнадцатеричную цифру запишем в виде двоичной тетрады и добавим необходимое количество незначащих нулей. Получим 00000000000000000000000010101010.
a) 1510 = 11112. Учитывая правила выполнения операции &, получим десятичное число 10 или шестнадцатеричное A, т. е. с помощью этой операции всегда выделяется последняя шестнадцатеричная цифра числа.
б) Так как 810=10002 и принимая во внимание правила выполнения операции | (битовое или), получим то же число 170.
в) Так как в двоичном представлении числа 8 есть всего одна единица, то результатом операции a & 8 будет либо 0, либо 8 =10002 в зависимости от того, что находится в 3–м справа бите числа a (нумерация битов с нуля). В нашем примере получим 8, и будет выведено “YES”, что означает, что в 3–м справа бите исходного числа a была единица.
i. 2. Определить результат:
unsigned short a; cin>>a;
a=a & 0xFFF7; cout<<a;
Решение. Введем, например, число 26. В двоичной системе счисления это 0000000000011010.
Так как FFF716=11111111111101112, то операция &(битовое и) оставит без изменения все биты, кроме третьего справа. Независимо от того, что было в этом бите (0 или 1), получится нуль, т. е. мы “выключили” третий справа бит. В результате получим 00000000000100102=18, т. е. будет выведено число 18. Если введем, например, число 23, то это число не изменится, так как в третьем бите был 0.
3. Упаковка информации. Пусть по условию задачи известно, что числа массива находятся в диапазоне от 0 до 15, т. е. каждое число занимает четыре бита. Надо обработать 20 таких чисел. Нет типа данных для хранения четырех битов. В каждом элементе массива типа unsigned short размером два байта (16 битов) можно разместить 16/4=4 числа из указанного диапазона. Поэтому для хранения 20 таких чисел понадобится массив из 20/4=5 элементов. 20 введенных чисел из диапазона 0..15 записать в массив типа unsigned short размерности 5.
unsigned short MyINP( unsigned short myMAX);
int main()
{ const int k=4, // размерность числа 0..15 4 бита
n=20, // количество чисел
size=n*k/(sizeof(unsigned short)*8); // размерность массива
unsigned short number, array[size];
for (int i=0;i<size;i++)
for(int j=1;j<=k;j++)
{ number=MyINP(15);
array[i]=(array[i]<<k) | number;
}
for (int i=0; i<size;i++)
// вывод в шестнадцатеричной системе счисления пяти
// шестнадцатеричных цифр
printf("%X ",array[i]);
getch(); return 0; }
unsigned short MyINP( unsigned short myMAX)
{ unsigned short x;
while (true) { cin>>x; if (x<=myMAX) return x;
textcolor(4); cprintf("ERROR Repeat");
} }
Функция MyINP вводит целое положительное число, меньшее или равное myMAX. При попытке ввести неположительное или большее myMAX число, красным цветом выводится сообщение об ошибке и приглашение повторить ввод.
4. Пусть int a=-8, b=23. Что получится в результате выполнения следующей части программы:
printf (“%d %X %d %X”, a & b, a | b, a ^ b, ~a).
5. Найти значение переменной r, объявленной как int r, если:
а) r= ~(100>>2) & ~(–10) | 0x10;
б) r=162 | 0x10A & 111;
в) r= –10 & 1|4;
г) r=(123<<4)& 123.
6. Найти значение этого же выражения из предыдущего упражнения, если переменная объявлена как unsigned short r.
7. Пусть в ячейке, объявленной short a, хранится следующая двоичная последовательность:
а) 0000001001001001; б) 1100000000001000;
в) 1111111111111000; г) 1000000000000000.
Что это за число в десятичной системе счисления? шестнадцатеричной системе счисления?
8. Выполнить предыдущее упражнение, если переменная объявлена как unsigned short a.
9. Как включить 4-й справа бит, т. е. поместить туда единицу независимо от того, какое там было значение, нуль или единица? Нумерация битов с нуля.
10. Как заменить значение 2–го справа бита на противоположное значение, т. е. нуль на единицу, а единицу на нуль? Нумерация битов с нуля.
11. С помощью битовых операций вывести на экран:
а) значение k–го справа бита;
б) i–ю справа шестнадцатеричную цифру.
12. Решить задачу упаковки (см. задачу 3) и распаковки, если диапазон чисел следующий: a) 0..1; б) 0..3; в) 0..7. Рассмотреть несколько вариантов объявления массива (unsigned short, char и др.).
13. Используя битовые операции, вывести шестнадцатеричное представление без знакового двухбайтного целого числа.
Указание. Последнюю шестнадцатеричную цифру получаем с помощью операции number & 0xF, или number & 15. Кроме того, на каждом шаге число сдвигаем на четыре разряда. Выделенную шестнадцатеричную цифру с помощью if и switch анализируем и выводим либо цифры от 0 до 9, либо буквы A, B, C, D, E, F.
14. В массиве целых положительных чисел найти все числа с наибольшим количеством единиц в двоичном представлении. Составить и использовать функцию, которая с помощью битовых операций находит количество единиц в двоичном представлении одного числа.
Логический тип
В классическом языке С такого типа не было. Вместо него использовался целый тип. Он появился в языке С++. Переменные логического (булевского) типа объявляются например следующим, образом: bool B1, B2, B3, B4. Переменная данного типа может принимать одно из двух значений — true или false, которыеявляются логическими константами. Кроме этих переменных дальше в тексте будем использовать вещественные переменные x и y, определяющие координаты точки на плоскости в декартовой системе координат.
Переменные такого типа можно использовать в следующих операторах:
· в операторах присваивания, например, B1=x>0; B1=true; B2=y>0 && x<0; B1=B2 || x*x+y*y<=4;
· в операторе if, например, if (B1) …, что равносильно
if (B1= = true) …;
· в операторах while и do … while, например, while (B1) { …}, что равносильно while(B1= = true) {…};
· в заголовке оператора for. Чаще всегобулевский тип используется во второй части заголовка. Например, for (x=1; B1; x+=0.5) {…}; что равносильно for (x=1; B1==true; x+=0.5) {…};
Теоретически переменные булевского типа могут быть в первой и в третьей частях заголовка этого оператора.
Для логических переменных и констант определены логические операции &&, || , ! (см. 3.3 ) и операции сравнения (> , < , >=, <=, = =, !=). Например, присваивание B3 = y>x*x && fabs(x)<2 можно записать следующим образом:
B1= y>x*x; B2= fabs(x)<2; B3=B1 && B2.
При сравнении логических величин учитывается, что результаты следующих сравнений истинны: true>false; true = = true;
false = = false. Например, пусть B1=x>0; B2=y>0. Выражение B1>B2 истинно тогда и только тогда, когда x>0 && y<=0 истинно, т. е. когда B1==true, а B2==false. Выражение B1 = = B2 истинно тогда и только тогда, когда x>0 && y>0 || x<=0 && y<=0, т. е. когда B1==true и B2==true или обе переменные принимают значение false. Выражение !(B1<B2) равносильно
x>0 && y<=0|| x>0 && y>0 || x<=0 && y<=0.
Существует связь между логическими операциями и оператором if.
Например, присваивание B3=B1 && B2 с помощью if без
логических операций можно записать так: if (B1) B3 = B2;
else B3 = false.
B3=!B1 || B2 равносильно
if (B2) B3=true;
else if (B1) B3 = false;
else B3 = true.
Мы уже знаем, что целый тип совместим с булевским типом. Любое ненулевое число играет роль true, а нуль равносилен false. Поэтому если int flag, то оператор if (flag) cout<<”Yes”;
else cout<<”No”; равносилен следующему:
if (flag!=0) cout<<”Yes”; else cout<<”No”. Этим можно объяснить отсутствие такого типа в старом языке C.
Следует различать логические операции и битовые.
Если логические операции используются для целых чисел, что на практике встречается не часто, то любое ненулевое число играет роль true, а нуль равносилен false. Например, в результате выполнения операторов
int b1=10<0xA, b2=0x10, b3; b3=b1 || b2 && 10; cout<<b3;
получим единицу. Почему? Так как 10 и 0xA — одно и то же число в разных системах счисления, то результат сравнения ложный и b1=0. Значением переменной b2 является ненулевое число 16 в шестнадцатеричной системе счисления, и это соответствует истине. Поэтому b2 && 10 даст в результате истину, и b3 как результат логической операции или будет также истинным, несмотря на то, что b1 = 0. Таким образом, будет выведена единица.
Если объявить bool b1=10<0xA, b2=0x10, b3; то результат будет тот же самый, т. е. будет выведена единица (а не true).
Наоборот, битовые операции можно использовать для логических значений, что на практике также используется редко. Например, рассмотрим следующий фрагмент:
bool b1=10<0xA, b2=0x10, b3;
b3=b1 | b2 & 10; cout<<b3;
В результате операции b2 & 10 получим 0, потому что битовая операция применяется к каждой паре битов, т. е. 12 & 10102=0. Значением b1 является false, т. е. 0. Поэтому в результате будет выведен 0. Необходимо обратить внимание, что значением переменной b2 будет 1, а не 100002 . По этой же причине, если bool b2=10, то в результате всё равно получим 0.
Но если int b1=10<0xA, b2=10, b3; то получим 10, так как 10&10=10, b1=0, 0 | 10 =10.
Символьный тип
В предыдущих разделах мы изучили:
· объявление переменных и массивов символьного типа;
· правила записи символьных констант (п. 3.1 гл. 1);
· ввод, вывод символьных переменных (§ 4 гл. 2);
· передачу символьной переменной в функцию (§ 1 гл. 2);
· анализ символов(§ 4 гл. 2);
· вывод кодов всех символов, т. е. кодовую таблицу (задача 8, § 2 гл. 2);
Среди символов есть такие, которые нельзя ввести обычным способом, т. е. с помощью соответствующих клавиш, так как их нет на алфавитно–цифровой клавиатуре. Они называются символами псевдографики.Для их ввода курсор подводим в нужное место и нажимаем “Alt”. При включённом цифровом режиме (клавиша NumLock) соответствующий код символа набираем справа на клавиатуре, а не в верхнем ряду. Во время набора ничего не отображается, но когда отпустим клавишу “Alt”, в соответствующем месте появится символ псевдографики с набранным кодом. Такие символы в функцию в качестве фактических параметров можно передать, как и обычные символы, двумя способами: можно указать числовой код символа или вышеописанным способом набрать такой символ.
Анализ кодовой таблицы позволяет отметить следующие особенности, которые необходимо учитывать при программировании:
· цифры в таблице располагаются подряд, цифра 0 имеет код 48, у цифры 1 код 49, и т. д., цифра 9 имеет код 57;
· как прописные, так и строчные латинские буквы располагаются в алфавитном порядке с кодами 65—90 для больших и
97—122 для маленьких букв;
· русские прописные буквы располагаются также подряд (коды 128—159);
· маленькие русские буквы терпят разрыв, буквы от ‘а’ (код 160) до ‘п’ (175) следуют подряд. После буквы ‘п’ размещаются символы псевдографики, а затем русский алфавит продолжается с буквы ‘р’ (224) до ‘я’(239).
Другие особенности символьной кодовой таблицы можно увидеть, выполнив вышеприведённую программу (задача 8, § 2 гл. 2).
Рассмотрим совместимость символьного и целого типов, что характерно для языка С++. В переменной типа char можно хранить как символы (точнее, их коды), так и целые однобайтные числа. Для переменных типа char определены те же операции, что и для типа int, например, целочисленное деление (/), получение остатка от целочисленного деления (%) и другие. Поэтому в языке С++, в отличие от Pascal, можно выполнить следующий фрагмент:
char a='5', b=5;
cout<<a<<”, “<<b<<”, “<<a%b<<", "<<a/b<<'\n';
В результате получим 5, символ с кодом 5(см. кодовую таблицу), 3, 10. Почему? В переменной a хранится код символа ‘5’, т. е. целое число 53, а в переменной b — обычное целое число 5,
а 53 % 5 = 3, 53 / 5 = 10. Заметим, что если объявим int a=53, b=5; то выведем 53, 5, 3, 10. Если, что интереснее, int a=’5’, b=5; то получим то же, что и в предыдущем варианте, т. е. 53, 5, 3, 10, так как значением целочисленной переменной a будет код символа 5, т. е. целое число 53.
При вводес помощью cin результат зависит от того, как переменная объявлена и что набираем при вводе.
Пусть char a, b; cin>>a>>b; При выполнении введём 5 и 3. Тогда с помощью того же cout получим 5, 3, 2, 1. Здесь число 2 получено не как остаток от деления 5 на 3, а как результат операции % для кодов этих символов, т. е. 53 % 51 = 2. Для этих же чисел получено и число 1. В этом можно убедиться, если вместо частного вывести произведение cout << a*b. На экране мы увидим не 15 (5*3), а 2703, т. е. произведение кодов символов 5 и 3 (51* 53 = 2703).
Пусть по–прежнему char a, b; cin>>a>>b; При выполнении наберем два символа, например, a и с, коды которых 97 и 99. Тогда по той же причине будет выведено: a, c, 97, 9603, где 97=97%99, а 9603=97*99. “Числа”, которые больше, чем 9, при таком объявлении ввести не сможем. Если введём, например, 123, то a примет значение символа ‘1’, b= ‘2’, а символ 3 игнорируется. Получим 1, 2, 49, 2450, где 49=49 % 50, 2450 = 49*50.
Если объявлено int a, b; и вводим целые числа, то значениями этих переменных будут введённые числа. При вводе символов ничего хорошего не получим, хотя программа будет выполняться.
Символы лучше вводить с помощью специальных функций getch, getchar или getche (см. § 4 гл. 2).
Вывод символов или их кодов удобнее выполнять не с помощью cout, а используя функции printf или cprintf (см. вывод кодовой таблицы).
Дата добавления: 2016-07-18; просмотров: 1501;