
Laboratornyy_praktikum_Programmirovanie_na_C
.pdfЛАБОРАТОРНАЯ РАБОТА 3. ЛОГИЧЕСКИЕ ВЫРАЖЕНИЯ
Цель работы: Научиться использовать логические выражения. Программы работы записать (вклеить распечатку) с пояснениями в лабораторный журнал.
3.1. ЛОГИЧЕСКИЕ ВЫРАЖЕНИЯ
Описание реальных объектов может рассматривать несколько путей развития событий в зависимости от значений свойств этих объектов, поэтому в программах довольно часто используются операторы отношения (сравнения) и логические операторы. Также эти типы операторов удобно применять для проверки допустимости диапазона значений данных для исключения ошибочных ситуаций.
Результатом выполнения оператора отношения или логического оператора между логическими операторами является логическое значение типа bool. Объекты, в том числе типа bool, можно сравнивать на равенство или неравенство, используя операторы отношения == и !=. А операторы сравнения <, >, <= или >= могут применяться ко всем числовым типам данных или между данными типа Char.
Ниже приведена таблица значений логических операций.
|
Исходные данные |
|
|
|
|
|
|
|
|
p ^ q |
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
p & p (И) |
|
|
p | q (ИЛИ) |
|
|
(исключающее |
|
|
!p (НЕ) |
|
|
p |
|
|
q |
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
ИЛИ) |
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
false |
|
false |
|
false |
|
false |
|
false |
|
|
true |
|||||
|
true |
|
false |
|
false |
|
true |
|
true |
|
|
false |
|||||
|
false |
|
true |
|
false |
|
true |
|
true |
|
|
true |
|||||
|
true |
|
true |
|
true |
|
true |
|
false |
|
|
false |
Различные логические операции могут быть построены с помощью встроенных. Например, импликация – двоичная операция, результатом которой является ложное значение только в том случае, если левый еѐ операнд имеет истинное значение, а правый – ложное (истина не может подразумевать ложь). Операцию импликации в C# можно записать как код «!p | q». Ниже приведена таблица истинности.
|
Исходные данные |
|
|
p ИМПЛИКАЦИЯ p (!p | q) |
|
|||
|
|
|
|
|||||
|
p |
|
|
q |
|
|
|
|
|
|
|
|
|
|
|
||
|
false |
|
|
false |
|
true |
||
|
true |
|
|
false |
|
false |
||
|
false |
|
|
true |
|
true |
||
|
true |
|
|
true |
|
true |
Укороченные операторы && (И) и || (ИЛИ) используются для ускорения работы программы, их второй операнд вычисляется только при необходимости. Так, если у оператора && первый операнд false или у оператора || первый операнд true, то результат предопределѐн, и значение второго операнда неважно.
using System; class P03_1
{
41
static void Main()
{
int x1 = 3, x2 = 5;
//Операция отношения (сравнения) bool a = x1 > x2;
Console.WriteLine("{0} > {1} = {2}", x1, x2, a);
/* Для нахождения значения в диапазоне, оно должно быть больше или равно нижней границы И меньше или равно верхней От обратного можно вычислить нахождение вне диапазона: значение меньше нижней границы ИЛИ больше верхней Для примера возьмём диапазон 4..10, границы которого заданы константами */
const int LowR = 4, HighR = 10;
Console.WriteLine("Задан диапазон " + LowR + ".." + HighR); Console.WriteLine("Нахождение {0} внутри диапазона: {1}",
x2, x2 >= LowR && x2 <= HighR); Console.WriteLine("Нахождение {0} вне диапазона: {1}",
x1, x1 < LowR || x1 > HighR);
Console.Write("Нажмите любую клавишу для завершения..."); Console.ReadKey(true); //Ожидает нажатия клавиши
}
}
В примере программы P03_1 представлены операции сравнения и логические операторы. Так как в языке используется естественные правила приоритета, в выражениях не возникла необходимость определять порядок вычислений скобками. Заметьте, что в программе использованы конст нты – именованные типизированные значения, которые могут быть использованы ниже места определения и применяются в программах, в которых одинаковые значения должны быть подставлены в нескольких местах. Константам принято давать имена, отражающие их назначение. Если переменная адресует область памяти, значение которой может быть считано или перезаписано, то значение константы изменить в программе нельзя, так как компилятор при компиляции заменяет все вхождения имѐн констант на их значения. Измените значения переменных x1 и x2 и констант LowR и HighR в качестве эксперимента.
Действие основных логических операторов И (&&) и ИЛИ (||) можно продемонстрировать графически. Если графические объекты воспринимать как множество точек, имеющих некоторые области с совпадающими координатами (Рис. 3.1а), то операция И (&&) отображается как пересечение этих областей (содержит точки, принадлежащие всем объектам, Рис. 3.1б), а операция ИЛИ (||) отображается как объединение (содержит точки, принадлежащие любому объекту, Рис. 3.1в).
42

Рисунок 3.1. Графическое изображение логических операций. а) Исходные фигуры б) Пересечение (И) в) Объединение (ИЛИ)
В технических расчѐтах обычно проверяется принадлежность значений одной или нескольким областям. Математическая запись, описывающая нахождение переменной x внутри диапазона –5 ≤ x ≤ 5 не может быть записана в этом виде в программу. Т.к. операции сравнения бинарны (т.е. в них участвуют два операнда), это выражение можно рассматривать как утверждение, что x больше или равен –5, И, одновременно, меньше или равен 5. В программе это выражение записывается следующим образом: (–5 <= x) && (x <= 5). Графически эту зависимость можно отобразить, как общую область диапазонов (Рис. 3.2).
Рисунок 3.2. Графическое выражение принадлежности значения отрезку, заданное логической операцией над диапазонами
3.2. ПОРАЗРЯДНЫЕ ЛОГИЧЕСКИЕ ОПЕРАТОРЫ
Операторы & (И), | (ИЛИ), ^ (Исключающее ИЛИ), >> (сдвиг вправо), << (сдвиг влево), ~ (дополнение до 1, унарный оператор НЕ, инверсия) при использовании с операндами целого типа выполняются поб тно и называются поразрядными. Подобные операторы очень часто используются при программировании микроконтроллеров и средств автоматики. Рассмотрим поразрядные операции над числами, представленными в двоичном виде (операция сдвига приведена на один
разряд): |
|
|
|
|
|
|
|
|
|
|
||
& |
0101 |
| |
0101 |
^ |
0101 |
|
|
|
|
|
|
|
1100 |
1100 |
1100 |
~ 0101 |
>> 000110 |
<< 000110 |
|||||||
|
|
|
||||||||||
|
0100 |
|
1101 |
|
1001 |
|
1010 |
|
000011 |
|
001100 |
Операция сдвига на каждый разряд влево умножает число на 2, вправо – делит на 2. Если тип первого операнда сдвига – int или long, сдвиг вправо является арифметическим сдвигом (пустым старшим разрядам задан знаковый бит). Если тип первого операнда – uint или ulong, сдвиг вправо является логическим сдвигом (старшие разряды заполняются нулями). Тип второго операнда сдвига должен быть int.
Операция «~» инвертирует все биты числа (0 в 1 и 1 в 0).
43

3.2.1.Упаковка данных логическими операторами
Внекоторых случаях необходимо разместить несколько состояний свойств в одном целом числе. Размер числа в битах (разрядность) определяет количество свойств, которое можно сохранить.
Ниже, в программе P03_2 приведѐн пример, демонстрирующий первоначальную сборку числа, содержащего свойства, а также установку, удаление и инверсию свойств. Допустим, в целое число необходимо упаковать свойства стиля оформления текста: размер шрифта, тип шрифта из предопределѐнного набора, цвет, флаги оформления жирный, курсив, подчеркнутый, заглавный. Для описания свойств используем константы и переменные, для группировки начиная их имена с символа «w». Ниже представлена таблица упаковки по битам с описанием.
|
Номер бита или |
|
Назначение |
|
|
|
|
||
|
диапазон |
|
|
|
|
|
|
|
|
|
|
|
Байт, хранящий размер шрифта (пт.) в диапазоне 0–255. |
|
|
|
|
Извлекается из целого числа маскированием – выполнением |
|
|
|
|
операции & (И) с константой 0xFF (байт, в котором все биты |
|
|
0–7 |
|
установлены в единицу). Так как старшие три байта целого числа |
|
|
|
|
при этом равны нулю, в результате они также равны нулю |
|
|
|
|
(операция И). |
|
|
|
|
Схема: 00000000 00000000 00000000 XXXXXXXX |
|
|
|
|
Полубайт, хранящий номер шрифта из списка в диапазоне значений |
|
|
|
|
0–15. Например, представим список заданных шрифтов Helvetica, |
|
|
8–11 |
|
Times, Consolas и пользовательские шрифты. Значение по |
|
|
|
|
умолчанию 0 даѐт шрифт Helvetica. |
|
|
|
|
Схема: 00000000 00000000 0000XXXX 00000000 |
|
|
|
|
Полубайт, хранящий номер цвета шрифта в диапазоне 0–15, |
|
|
12–15 |
|
значение по умолчанию 0 даѐт чѐрный цвет. |
|
|
|
|
Схема: 00000000 00000000 XXXX0000 00000000 |
16Бит, определяющий жирный шрифт. По умолчанию 0 – выключено.
Схема: 00000000 0000000X 00000000 00000000
17Бит, определяющий курсив. По умолчанию 0 – выключено.
Схема: 00000000 000000X0 00000000 00000000
18Бит, определяющий подчѐркивание. По умолчанию 0 – выключено.
Схема: 00000000 00000X00 00000000 00000000
Бит, определяющий использование заглавных символов. По
19умолчанию 0 – выключено.
Схема: 00000000 0000X000 00000000 00000000
20–31 Зарезервировано.
Первый номер бита в диапазоне указывает смещение от младшего бита и может использоваться для извлечения числа с помощью операции смещения >> и маскирования &.
Для удобства определения свойств в программе заданы в шестнадцатеричном виде константы. Это удобно тем, что каждый байт записывается двумя полубайтами, и значения отдельно заданных битов составляют ряд значений 1, 2, 4, 8 для каждого полубайта.
using System;
44
class P03_2
{
static void Main()
{
int wTextStyle; // число для хранения свойств // Определение констант шрифта
const int |
wTimes = 0x100, wConsolas = 0x200; |
||
// Определение констант цвета |
|
||
const int |
wBlack = 0x0, wBlue |
= 0x1000, |
|
wGreen = 0x2000, wCyan = |
0x3000, |
||
wRed = 0x4000, wMagenta = |
0x5000, |
||
wBrown = 0x6000, wLGray = |
0x7000, |
||
wGray |
= |
0x8000, wLBlue = |
0x9000, |
wLGreen |
= 0xA000, wLCyan |
= 0xB000, |
|
wLRed |
= |
0xC000, wLMagenta |
= 0xD000, |
wYellow |
= 0xE000, wWhite |
= 0xF000; |
//Т.к. первые два байта заняты размером,
//номером и цветом шрифта, флаги свойств
//начинаются с третьего байта
const int |
wBold = 0x10000, |
// жирный |
||
wItalic |
= 0x20000, |
// курсив |
||
wUnderline = 0x40000, |
// |
подчёркнутый |
||
wCaps |
= |
0x80000; |
// |
заглавный |
Console.WriteLine("Установить размер 14, Times, "+
"наклонный:");
wTextStyle = 14 + wTimes + wItalic;
//Вывод в двоичном виде с дополнением нулями
Console.WriteLine(Convert.ToString(wTextStyle, 2).PadLeft(32, '0'));
Console.WriteLine("Очистив, установить желтый:"); wTextStyle = (int)(wTextStyle & 0xFFFF0FFF) + wYellow; Console.WriteLine(Convert.ToString(wTextStyle,
2).PadLeft(32, '0')); Console.WriteLine("Установить размер 12"); wTextStyle = (int)(wTextStyle & 0xFFFFFF00) + 12; Console.WriteLine(Convert.ToString(wTextStyle,
2).PadLeft(32, '0')); Console.WriteLine("Изменение флагов");
//Изменение на противоположный флага подчёркнутый wTextStyle ^= wUnderline;
//Установка флага жирный
wTextStyle |= wBold;
// Удаление флага курсив wTextStyle &= ~wItalic;
Console.WriteLine(Convert.ToString(wTextStyle,
45
2).PadLeft(32, '0')); Console.WriteLine("Жирный :" +
((wTextStyle & wBold) != 0)); Console.WriteLine("Курсив :" +
((wTextStyle & wItalic) != 0)); Console.WriteLine("Подчёркнутый:" +
((wTextStyle & wUnderline) != 0)); Console.Write("Нажмите любую клавишу для завершения..."); Console.ReadKey(true);
}
}
3.2.2.Вычисление адреса подсети и номера компьютера
Вкачестве примера в проекте P03_3 вычисляется для IP4-адреса 172.16.235.1/23 адрес подсети и номер компьютера. Наберите приведенный ниже пример, после выполнения измените литералы для IP 192.168.1.241/25. Так как не все строковые литералы помещаются в строку, они разделены на несколько строк с помощью операций конкатенации (+), в программе их можно записывать без разрыва.
using System; class P03_3
{
static void Main()
{
int |
i1 |
= |
172, |
i2 |
= |
16, i3 = 235, i4 = 1; |
//IP |
int |
m1 |
= |
255, |
m2 |
= |
255, m3 = 254, m4 = 0; |
//Маска |
Console.WriteLine("IP: {0}.{1}.{2}.{3}/{4}.{5}.{6}.{7}",
i1,i2,i3,i4,m1,m2,m3,m4); |
|
|
|
||||||||
int |
p1 |
= |
i1 |
& |
m1, |
p2 |
= |
i2 |
& |
m2; |
//Подсеть |
int |
p3 |
= |
i3 |
& |
m3, |
p4 |
= |
i4 |
& |
m4; |
|
Console.WriteLine("Подсеть: {0}.{1}.{2}.{3}",p1,p2,p3,p4); int a1 = i1 & ~m1, a2 = i2 & ~m2; //Номер компьютера
int a3 = i3 & ~m3, a4 = i4 & ~m4;
//Номер компьютера в 32-х битном виде
int a = a1 * 0x1000000 + a2 * 0x10000 + a3 * 0x100 + a4; Console.WriteLine("Номер компьютера: {0}.{1}.{2}.{3}"+ " ({4})", a1, a2, a3, a4, a);
Console.Write("Нажмите любую клавишу для завершения...");
Console.ReadKey(true);
}
}
Для нахождения адреса подсети между IP и маской выполняется операция & (поразрядное И), для нахождения номера компьютера также выполняется операция & между IP и инвертированной (операция НЕ, при которой единицы превращаются в нули и наоборот) маской. Для удобства номер компьютера из побайтного
46
представления переводится в 32-битное число: вычисляется сумма составляющих байт, умноженное на порядок байта в 32-х битном числе, представленный для удобства в шестнадцатеричном виде. Автоматизируйте процесс разбора адреса, написав программу P03_4 универсальной для любого введенного с клавиатуры IP.
using System; class P03_4
{
static void Main()
{
Console.Write("Введите IP (n.n.n.n/m): "); var IPs = Console.ReadLine(); //IP: n.n.n.n/m int i, i1, i2, i3, i4, m,
IP, mask = -1, SubNet, NComp;
//Каждый октет выделяется поиском '.'
//Маска - '/'. Проверки формата нет!
//Parse преобразует строку в число i = IPs.IndexOf('.');
i1 = int.Parse(IPs.Substring(0, i));
IPs = IPs.Substring(i + 1); i = IPs.IndexOf('.'); i2 = int.Parse(IPs.Substring(0, i));
IPs = IPs.Substring(i + 1); i = IPs.IndexOf('.'); i3 = int.Parse(IPs.Substring(0, i));
IPs = IPs.Substring(i + 1); i = IPs.IndexOf('/'); i4 = int.Parse(IPs.Substring(0, i));
IPs = IPs.Substring(i + 1); m = int.Parse(IPs);
IP = (i1 << 24) + (i2 << 16) + (i3 << 8) + i4;
//Путём сдвига маска заполняется битами mask = (int)((uint)mask << 32 - m);
//Для вывода значение преобразуется в двоичную строку
//и заполняется слева нулямипри необходимости:
Console.WriteLine("IP |
: " |
+ |
Convert.ToString(IP, |
2).PadLeft(32,'0')); |
|
|
|
Console.WriteLine("маска |
: " |
+ |
Convert.ToString(mask, |
2).PadLeft(32, '0')); |
|
|
|
//С помощью маски из IP выделяем подсеть и номер комп. SubNet = IP & mask;
NComp = IP & ~mask;
Console.WriteLine("подсеть: " + Convert.ToString(SubNet, 2).PadLeft(32, '0'));
Console.WriteLine("№ комп.: " + Convert.ToString(NComp, 2).PadLeft(32, '0'));
//Конвертируем 32-битные целые в побайтное представление string IPs2, MaskS, SubNetS, NCompS;
47
//выделение октетов с помощью логических операторов:
//нужный байт сдвигается в младший и
//при необходимости маскируется в младшем байте
//(удаляются оператором И биты, ему не принадлежащие)
IPs2 = ((uint)IP >> 24).ToString() + "." + ((IP >> 16) & 0xFF).ToString() + "." + ((IP >> 8) & 0xFF).ToString() + "." + (IP & 0xFF).ToString();
Console.WriteLine("IP |
: " + IPs2); |
|
|
|
MaskS = ((uint)mask |
|
>> 24).ToString() |
+ "." |
+ |
((mask >> 16) & |
0xFF).ToString() + |
"." + |
|
((mask >> 8) & 0xFF).ToString() + "." + (mask & 0xFF).ToString();
Console.WriteLine("маска : " + MaskS);
SubNetS = ((uint)SubNet >> 24).ToString() + "." + ((SubNet >> 16) & 0xFF).ToString() + "." + ((SubNet >> 8) & 0xFF).ToString() + "." + (SubNet & 0xFF).ToString();
Console.WriteLine("подсеть: " + SubNetS); NCompS = ((uint)NComp >> 24).ToString() + "." +
((NComp >> 16) & 0xFF).ToString() + "." + ((NComp >> 8) & 0xFF).ToString() + "." + (NComp & 0xFF).ToString();
Console.WriteLine("№ комп.: "+NCompS+" ({0})",NComp); Console.Write("Нажмите любую клавишу для завершения..."); Console.ReadKey(true);
}
}
Следует заметить, что при преобразовании числового адреса в четыре октета, разделѐнные точкой, у старшего байта, при выполнении сдвига вправо на 24 бита, использовалось преобразование в беззнаковое целое, потому что старший бит целого со знаком является флагом отрицательного числа и остаѐтся при сдвиге. Операция & (И) с числом 0xFF позволяет выделить (сохранить) все биты младшего байта, т.к. в 0xFF все биты установлены в единицу, все старшие байты при этом обнуляются.
3.2.3. Проверка нечётности
Проверку чѐтности/нечѐтности числа можно организовать несколькими способами, в примере P03_5 приведены способы сравнения с единицей остатка от деления нацело на два и извлечение младшего бита, который отвечает за нечѐтность, поразрядным оператором И.
using System; class P03_5
{
static void Main() {
48
int i = 5, j = 4;
Console.WriteLine("Число нечётное {0} : {1}", i, (i % 2 == 1));
Console.WriteLine("Число нечётное {0} : {1}", j, ((j & 1) == 1));
Console.Write("Нажмите любую клавишу для завершения..."); Console.ReadKey(true);
}
}
3.2.4. Логические операторы в шифровании данных
Поразрядный оператор ^ Исключающее ИЛИ (XOR) используется в системах шифрования. Обычно в качестве ключа используется массив данных, полученный с помощью специальных алгоритмов, содержание которого может меняться в процессе шифрования. Рассмотрим пример простейшего шифрования. В качестве ключа используем число. Оператор ^, шифрующий символы, обратим, и повторное выполнение производит дешифрование.
using System; class P03_6
{
static void Main()
{
char a1 = 'С', a2 = 'Е', a3 = 'К', a4 = 'Р', a5 = 'Е', a6 = 'Т';
Console.WriteLine("Слово:" + a1 + a2 + a3 + a4 + a5 + a6); char K = '\x33';
a1 ^= K; a2 ^= K; a3 ^= K; a4 ^= K; a5 ^= K; a6 ^= K; Console.WriteLine("Слово:" + a1 + a2 + a3 + a4 + a5 + a6); a1 ^= K; a2 ^= K; a3 ^= K; a4 ^= K; a5 ^= K; a6 ^= K; Console.WriteLine("Слово:" + a1 + a2 + a3 + a4 + a5 + a6); Console.Write("Нажмите любую клавишу для завершения..."); Console.ReadKey(true);
}
}
3.3. ПРОГРАММИРОВАНИЕ
3.3.1. Задача. Чётность
а) Написать программу P03_7, в которой целое число вводится с клавиатуры, а на экран выводится результат преобразования чѐтных чисел в нечѐтные (в сторону увеличения) и нечѐтных чисел в чѐтные (в сторону уменьшения). Для преобразований использовать поразрядные операторы. Протестировать на различных числах.
б) Написать программу P03_8, в которой с клавиатуры вводится четыре целых числа, найти и вывести количество нечѐтных чисел.
в) В программе P03_9 аналогично б), количество отрицательных чисел.
49
3.3.2. Задача. Поразрядные операторы
Написать программу P03_10, в которой целое число вводится с клавиатуры, выделяется из него третий байт, выводится его. В качестве первого тестового числа взять 16061031. Протестировать другие числа в диапазоне значений типа int.
3.3.3. Задача. Взаимозаменяемость логических операторов
Дано целое беззнаковое число j, выражение ~j инвертирует биты числа. Записать выражение, приводящее к такому же результату, пользуясь другими поразрядными логическими операторами. Проверить на работоспособность (программа P03_11).
3.3.4. Задача. Логические элементы в электронике**
Схемы автоматизации часто строят на основе микропроцессоров, но элементарные цифровые преобразования можно производить с помощью микросхем, содержащих логические элементы. Микросхема может содержать как один, так и несколько как одинаковых, так и разных элементов. Элементы обозначаются прямоугольниками. Подведѐнные к прямоугольникам линии слева считают входными, справа – выходными. Если в точке входа или выхода присутствует пустой кружок, то этот вход или выход является инвертирующим (изменяет значение сигнала на противоположное). Если в блоке единственный выход или все выходы инвертирующие, то к названию логического блока добавляется -НЕ: И-НЕ, ИЛИ-НЕ и т.д. Если прямоугольник пуст, то он не производит логической обработки, но обычно такие элементы имеют один вход и один или несколько инвертирующих выходов, такой элемент называется НЕ, инвертор также может обозначаться треугольником с пустым кружком на выходе. Если в прямоугольнике присутствует знак «&», то это логический элемент И (AND), логическая операция производится между всеми входами элемента. Знаком «1» (или «>=1») обозначают логические элементы ИЛИ (OR), знаком «=1» – элементы Исключающее ИЛИ (XOR), знаком «=» – Исключающее ИЛИ-НЕ (с инвертирующим выходом). Если выходов несколько, то у них одинаковый сигнал (исключение – инвертированные выходы). Наличие напряжение считается логической единицей, отсутствие – нулѐм. Входные и выходные линии нумеруются в соответствии с битами в числе. Для обработки байта информации необходимо восемь линий.
Составьте программу с логическими вычислениями P03_12, которая будет обрабатывать введѐнное с клавиатуры целое число (байт, в диапазоне 0..255) аналогично приведенной на Рис. 3.3 схеме логических элементов (Рис. 3.3) и выведите результат на экран.
50