Лр 2 bsl ошибки в программе переменные булевская арифметика
Строковые переменные.
Объекты класса String объявляются как все прочие
объекты простых типов - с явной или отложенной
инициализацией, с явным или неявным вызовом
конструктора класса. Чаще всего, при объявлении
строковой переменной конструктор явно
не вызывается, а инициализация задается
строковой константой. Но у класса String
достаточно много конструкторов. Они позволяют
сконструировать строку из:
- символа, повторенного заданное число раз;
- массива символов char [ ];
- части массива символов.
Операции над строками:
- две операции проверки эквивалентности (==) и (!=);
- конкатенация или сцепление строк (+);
Символьные переменные.
Значением символьной переменной является один
символ из фиксированного набора. Такой набор
обычно включает буквы, цифры, знаки препинания,
знаки математических операций и различные
специальные символы (процент, амперсенд,
звездочка, косая черта и др.). Подчеркнем, что,
в отличие от строковой переменной, символьная
всегда содержит ровно один символ.
(Строковая содержит строку из нескольких символов.)
Конечно, в памяти компьютера никаких символов
не содержится. Символы представляются их
целочисленными кодами в некоторой фиксированной
кодировке. Кодировка определяется тремя параметрами:
- диапазоном значений кодов
- множеством изображаемых символов
- отображением множества кодов на множество символов.
Оператор присваивания указывается как
практически также, как в любом другом языке
программирования. Оператор присваивания имеет
Тип переменной должен быть совместим
с типом выражения.
Оператор присваивания имеет одно интересное
свойство: позволяет создавать «цепочку присваиваний».
(переменным x, y, z присваивается значение 100)
Такая последовательность переменных и оператор
допускается, поскольку оператор = присваивает
переменной, находящийся слева него, значение
выражения, находящегося справа. Следовательно,
выражение z =100 будет имеет значение 100, которое
присваивается переменной y, а затем - переменной x.
Используя «цепочку присваиваний», можно одним
значением легко инициализировать группу переменных.
К неявным относятся те преобразования, результат
выполнения которых всегда успешен и не приводит к
потере точности данных. Неявные преобразования
выполняются автоматически. Для арифметических
данных это означает, что в неявных преобразованиях
диапазон типа назначения содержит в себе диапазон
исходного типа. Например, преобразование из типа
byte в тип int относится к неявным, поскольку
диапазон типа byte является подмножеством
диапазона int. Это преобразование всегда успешно и
не может приводить к потере точности. Заметьте,
преобразования из целочисленных типов к типам с
плавающей точкой относятся к неявным. Хотя здесь
и может происходить некоторое искажение значения,
но точность представления значения сохраняется,
например, при преобразовании из long в double порядок
значения остается неизменным. К явным относятся
разрешенные преобразования, успех выполнения
которых не гарантируется или может приводить к потере
точности. Такие потенциально опасные преобразования
должны быть явно заданы программистом.
Преобразование из типа int в тип byte относится к явным,
поскольку оно небезопасно и может приводить к потере
значащих цифр. Заметьте, не для всех типов существуют
Условный переход можно реализовать в
программе с помощью ключевых слов языка:
if, else или switch. Такой переход возможен только при
условии, если он является истинным.
if. else оператор
if. else — это оператор ветвления, работа которого
определяется условием. Условие оператора
анализируется инструкцией if. Если условие верно
(true), то выполняется блок инструкций программы,
описанных после условия.
if ( expression ) statement 1
Такой вид этого оператора вы можете найти в
Он показывает, что работа условного оператора
определяется булевым выражением (выражение,
которое имеет значение true или false) в круглых скобках.
Если значение этого выражения истинно, то выполняется
блок инструкций statementl. Если же выражение ложно,
произойдет выполнение блока инструкций statement2.
Необходимо заметить, что вторая часть оператора
(else statement^) может не указываться. Если инструкций
в блоках statementl или statement2 больше одной, то блок
обязательно нужно брать в фигурные скобки.
static void Mainf )
int valueOne = 20;
//устанавливаем второе значение больше первого
int valueTwo = 10;
if ( valueOne > valueTwo )
"valueOne: больше чем valueTwo: fl>",
"valueTwo: больше или равно valueOne: ",
//устанавливаем первое значение больше второго
if ( valueOne > valueTwo )
"valueOne: больше чем valueTwo: (1)",
//делаем значения одинаковыми
iffvalueOne valueOne и valueTwo равны: ==",
Если еще раз внимательно посмотреть на примеры
(while, do. while, goto), можно заметить постоянно
повторяющиеся операции: первоначальная
инициализация переменной i, ее наращивание на 1
внутри цикла, проверка переменной i на
выполнение условия (i < 10). Цикл for позволяет
вам объединить все операции в одной инструкции.
for ( [инициализация ]; [ выражение]; [ наращивание] )
Выполним тот же пример, но уже с использованием
public class ForCycle
public static int Main()
for (int i = 0; i < 10; i++)
Результатом выполнения такого цикла будет вывод
на экран информации вида:
Принцип работы такой инструкции очень прост:
1. Происходит инициализация переменной i.
2. Выполняется проверка соответствия условию.
Если условие истинно, то происходит выполнение
блока вложенных инструкций; если условие
оказалось ложным, то цикл прекращается и
выполняется программа за фигурными скобками.
3. Переменная i увеличивается на 1.
Наращивание переменной внутри цикла происходит
на такое число единиц, на которое вы сами зададите.
Операция i++ означает «увеличить значение
переменной на 1». Если вы хотите использовать
другой шаг изменения i, то смело можете написать
так i += 2. В этом случае значение переменной i
будет изменяться на 2 единицы, и на экране
Тут вы можете оставить комментарий к выбранному абзацу или сообщить об ошибке.
В предыдущей главе, при изучении условных операторов, мы говорили о том, что <условие> имеет два значения: истина и ложь (да, нет) . Здесь важно то, что значений всего лишь два. Этим условия отличаются от чисел. Существенно, что числа могут принимать много значений, (сейчас для наших рассуждений не важно, сколько именно), важно, что много.
Рассмотрим, например выключатель, которым мы включаем нашу обычную осветительную лампу. Выключатель также имеет два состояния: включен и выключен.
Переменные, которые имеют два состояния, называются логическими переменными или переменными булевского типа.
Для них в языке Паскаль существует специальный тип bolean. Соответственно такие переменные описываются в секции описаний:
var b1,b2:boolean;
Здесь описаны две логические переменные b1 и b2.
Операции обычной арифметики к таким переменным неприменимы. Некоторые из них мы разберем на примерах.
Рассмотрим такую схему, состоящую из двух выключателей X1 и X2 включенных последовательно и лампочки. Каждый выключатель может быть включен или выключен, так что всего мы будем иметь четыре комбинации. Распишем эти комбинации в таблицу.
Лампочка будет гореть только в том случае, если включены оба выключателя. И первый , И второй. Такая схема включения называется логическое “И”. По-английски AND . Могут встречаться и другие обозначения:
В языке Паскаль эта операция обозначается как “ AND ” и записывается так.
Var X1,X2,Result:Boolean;
…
Result := X1 AND X2;
…
Рассмотрим теперь другую схему, также с двумя выключателями, но соединенными по другому.
Понятно, что теперь лампочка будет гореть, если хотя бы один выключатель включен. Опять составим таблицу возможных вариантов.
Эта таблица отличается от предыдущей. Посмотрите внимательно и обратите внимание на их различие. Такое включение называется “логическое ИЛИ”. По-английски OR . Могут встречаться и другие обозначения:
В языке Паскаль эта операция обозначается как “ OR ” и записывается так.
Var X1,X2,Result:Boolean;
…
Result := X1 OR X2;
…
Схемы с лампочками и выключателями приведены только для того, чтобы облегчить понимание того, что такое логические функции. Главное, это таблица истинности. Любая логическая функция задается с помощью таблицы истинности.
Есть еще две важные операции. Отрицание и “исключающее или”.
Смысл операции отрицания понятен из названия. В Паскале она записывается как “ NOT ”. Могут использоваться также другие обозначения:
Эта функция одноместная, то есть имеет один аргумент.
Исключающее или в Паскале записывается как “XOR”. Ее смысл таков функция имеет значение истина, когда операнды не равны друг другу.
Программирование в значительной степени связано с доказуемостью, с выводимостью. Мы должны иметь возможность понимать совсем не простое, проходящее через множество ветвлений поведение программ во время их выполнения. Человек физически не в состоянии проследить за мириадами базисных операций, выполняемых компьютером.
В принципе, все может быть выведено из текста программы простыми рассуждениями. Если бы существовала наука выводимости, она оказала бы нам существенную помощь.
Можно радоваться: есть такая наука – это логика. Логика – это механизм, стоящий за способностью человека делать выводы. Когда нам говорят, что Сократ – человек и все люди смертны, то без раздумья мы заключаем, что Сократ смертен. Сделать этот вывод нам помогли законы логики . Пусть справедливо утверждение: "Если температура в городе поднимается выше 30 градусов, то возникает угроза загрязнений". Кто-то говорит, что поскольку сегодня температура достигла только 28 градусов, угрозы загрязнений нет; мы скажем про такого человека, что его логика "хромает".
Логика – основа математики. Математики доверяют доказательствам в пять строчек или доказательствам , растянутым на 60 страниц, только потому, что каждый шаг доказательства выполнен в соответствии с правилами логики.
Логика – основа разработки ПО . Уже в предыдущей лекции мы познакомились с условиями в контрактах, связанных с нашими классами и методами, например, в предусловии : "i должно быть между 1 и count". Мы будем также использовать условия, выражая действия, выполняемые программой, например: "Если i положительно, то выполни этот оператор".
Мы уже видели при изучении контрактов, что такие условия появляются в наших программах в форме " булевских выражений ". Булевское выражение может быть сложным, включающим, например, операции " not ", " and ", " or ", " implies ". Это соответствует выводам, характерным для естественного языка: " Если пройдет 20 минут после назначенного срока, и она не позвонит или не пришлет SMS , то следует, что она не появится совсем". Интуитивно мы прекрасно понимаем, что означает это высказывание , и этого понимания вполне достаточно и для условий, применяемых в ПО .
Но до определенных пределов. Разработка ПО требует доказуемости, а точные выводы требуют законов логики. Поэтому, прежде чем вернуться к нашим дорогим объектам и классам, следует более тесно познакомиться с законами логики .
Логика – математическая логика, если быть более точным, – самостоятельная наука, таковой является и ее ветвь "Логика в информатике ", которой посвящены многие учебники и отдельные курсы. Я надеюсь, что такой курс вами уже пройден или будет пройден. Эта лекция вводит основные понятия логики, необходимые в программировании. Хотя логика пользуется заслуженной славой как наука о выводимости, нам она нужна для более ограниченных целей – для понимания той части выводимости, которая базируется на условиях. Логика даст нам прочную основу для выражения и понимания условий, появляющихся в контрактах и в операторах программы.
Первая часть лекции вводит булеву алгебру в форме пропозиционального исчисления, которое имеет дело с базисными высказываниями , включающими специфические переменные. Вторая часть расширяет обсуждение до логики предикатов, позволяющей выражать свойства произвольного множества значений.
5.1. Булевские операции
Условие в булевой (булевской) алгебре , так же как и в языках программирования, выражается в виде булевского выражения , построенного из булевских переменных и операций. Результатом вычисления булевского выражения является булевское значение .
Булевские значения, переменные, операции, выражения
Существуют ровно две булевские константы (boolean constants), также называемые "булевскими или истинностными значениями ", которые будем записывать в виде True и False для совместимости с нашим языком программирования, хотя логики часто пишут просто T и F, а электротехники предпочитают обозначать их как 1 и 0.
Булевская переменная задается идентификатором, обозначающим булевское значение. Типично мы используем булевскую переменную, чтобы выразить свойство, которое может иметь в качестве значения истину или ложь . Говоря о погоде, мы могли бы ввести переменную rain_today , чтобы задать свойство, говорящее нам, будет ли сегодня идти дождь.
Начав с булевских констант и переменных, мы можем затем использовать булевские операции для получения булевских выражений . Например, если rain_today и cuckoo_sang_last_night ("дождь сегодня" и "кукушка куковала сегодня ночью") являются булевскими переменными, то булевскими выражениями в соответствии с изучаемыми ниже правилами будут:
- rain_today – булевская переменная сама по себе без всяких операций является булевским выражением (простейшая форма наряду с булевскими константами);
- not rain_today – используется булевская операция not ;
- ( not cuckoo_sang_last_night ) implies rain_today – используются операции not и implies , а также скобки для выделения подвыражений .
Каждая булевская операция, такая как not, or, and, =, implies, задает правила вычисления значения результирующего выражения по заданным значениям ее операндов .
В языке программирования булевские операции, подобно булевским константам, задаются ключевыми словами. В математических текстах знаки операций обычно задаются отдельными символами, не все из которых присутствуют на клавиатуре компьютера. Приведем соответствие между ключевыми словами и общеупотребительными символами в математике:
В Eiffel булевские константы, переменные и выражения имеют тип BOOLEAN, определенный классом, подобно всем типам. Класс BOOLEAN является библиотечным классом, доступным для просмотра в EiffelStudio; там вы можете увидеть все булевские операции, обсуждаемые в этой лекции.
Отрицание
Рассмотрим первую операцию – not . Для формирования булевского выражения с not укажите эту операцию с последующим булевским выражением . Это выражение может быть булевской переменной, как в not your_variable; или может быть сложным выражением (заключенным в скобки для устранения двусмысленности), как в следующих примерах, где a и b являются булевским переменными:
Для произвольной булевской переменной a значение not a есть False , если значение a есть True , и True , если значение a – False , мы можем выразить свойства операции not следующей таблицей:
Это так называемая таблица истинности (truth table), которая является стандартным способом задания булевских операций – в первых столбцах (для данной операции один столбец) задаются все возможные значения операндов операции, в последнем столбце задается соответствующее данному случаю значение выражения .
Операция not задает отрицание – замену булевского значения его противоположностью, где True и False противоположны друг другу.
Из таблицы истинности следуют важные свойства этой операции.
Теоремы: "Свойства отрицания"
- точно одно из выражений e или not e имеет значение True ;
- точно одно из выражений e или not e имеет значение False ;
- только одно из выражений e или not e имеет значение True (принцип исключенного третьего);
- Либо e , либо not e имеет значение True (принцип непротиворечивости)
Доказательство : по определению булевского выражения , e может иметь только значение True или False . Таблица истинности показывает, что если e имеет значение True , то not e будет иметь значение False ; все четыре свойства являются следствиями этого факта (и два последних утверждения следуют непосредственно из первого).
Дизъюнкция
Операция or использует два операнда в отличие от операции not . Если a и b являются булевскими выражениями , булевское выражение a or b имеет значение True , если и только если либо a , либо b имеют это значение. Соответственно, оно имеет значение False , если и только если оба операнда имеют это значение. Это отражает следующая таблица истинности :
Первые два столбца перечисляют все четыре возможные комбинации значений a и b .
Слово " or " заимствовано из естественного языка в его неисключительном смысле, как в предложении "Тот, кто придумал эту инструкцию, глуп или слеп", что не исключает, что оба случая могут иметь место.
Обычный язык часто использует "или" в исключающем смысле, означающее, что только один результат может быть истинным, но не оба вместе: "Что будем заказывать – красное или белое?". Здесь речь идет о другой булевской операции – " исключающему или " – "xor" в Eiffel , чьи свойства следует изучить самостоятельно.
Неисключающая операция or называется дизъюнкцией. Это не очень удачное имя, поскольку позволяет думать об исключающей операции, но в нем есть свое преимущество, благодаря симметрии с " конъюнкцией " – именем следующей операции and .
Дизъюнкция имеет значение False только в одном из четырех возможных случаев, заданном последней строкой таблицы истинности .
Теорема: "Принцип дизъюнкции"
Дизъюнкция or имеет значение True , за исключением случая, когда оба операнда имеют значение False .Таблица истинности показывает, что операция or является коммутативной: для любых a и b значение a or b то же, что и b or a . Это также вытекает из принципа дизъюнкции .
Переменные логического типа данных
Для объявления логической переменной используется ключевое слово bool:
Инициализировать логическую переменную или выполнить операцию присваивания можно с помощью ключевых слов true или false :
Аналогично работе унарного оператора минус ( - ), с помощью которого мы можем сделать число отрицательным, с помощью логического оператора НЕ ( ! ) мы можем изменить true на false и наоборот ( false на true ):
На самом деле, логические значения не сохраняются как true или false . Они обрабатываются в виде целых чисел: вместо true — единица, вместо false — ноль.
Следовательно, если мы попытаемся вывести логические значения с помощью std::cout, то увидим либо 0 , либо 1 :
Результат выполнения программы:
Если вы хотите, чтобы std::cout выводил true или false (вместо целых чисел), то тогда используйте манипулятор форматирования std::boolalpha:
std :: cout << std :: boolalpha ; // выводим логические значения как "true" или "false"Результат выполнения программы:
Использование логического типа данных в ветвлениях if
Очень часто логические переменные используются в ветвлениях if. Ветвление if выглядит следующим образом:
if (выражение) стейтмент1;
if (выражение) стейтмент1;
else стейтмент2;
В обоих случаях, если результатом условия является ненулевое значение, то выполняется стейтмент1 . Если же результатом условия является нулевое значение, то выполняется стейтмент2 .
Теперь рассмотрим пример в коде:
The condition is true!
Что здесь делается? Во-первых, мы начинаем с условия if, которым является логическое значение true , т.е. 1 (ненулевое значение), что означает, что выполняться будет стейтмент1 .
Следующая программа работает аналогично:
Здесь, при проверке условия, переменная b имеет значение false . false — это 0 . Следовательно, первый стейтмент под if (который true ) пропускается, а второй, который под else ( false ) — выполняется.
А теперь рассмотрим пример посложнее. Оператор равенства ( == ) используется для сравнения двух чисел (являются ли они равными). Оператор == возвращает true , если операнды равны и false , если таковыми не являются:
Результат выполнения программы:
Enter an integer: 4
The value is non-zero
Давайте разберемся, что и как здесь работает. Во-первых, мы просим пользователя ввести целое число. После этого, с помощью оператора == , мы проверяем, является ли пользовательское число нулевым. В вышеприведенном примере 4 не равно 0 , поэтому оператор == определяет условие как false . Следовательно, выполняется стейтмент2 (тот, который под else), где мы выводим The value is non-zero .
Возвращаемые значения логического типа данных
Логические значения часто используются в качестве возвращаемых значений в функциях. Названия таких функций очень часто начинаются со слов is (например, isEqual ) или has (например, hasCommonDivisor ).
Рассмотрим следующий пример:
// Возвращаем true, если x и y равны, в противном случае - возвращаем false return ( x == y ) ; // оператор == возвращает true, если x равно y, в противном случае - falseРезультат выполнения программы:
Enter an integer: 5
Enter another integer: 5
5 and 5 are equal
Как это работает? Во-первых, мы указываем значения переменным х и у . Затем проверяется условие, что приводит к вызову функции isEqual(5, 5). Внутри этой функции наши два числа сравниваются между собой ( 5 == 5 ), что приводит к возврату значения true (так как 5 = 5 ). Значение true возвращается обратно в caller. Так как условие истинно, то выполняется стейтмент1 , который выводит 5 and 5 are equal .
К логическим значениям нужно немного привыкнуть, но как только вы это сделаете, то сами удивитесь, насколько они удобны и просты.
Во всех примерах, приведенных выше, в наших условиях были либо логические значения ( true или false ), либо логические переменные, либо функции, которые возвращают логическое значение. А что произойдет, если мы не будем использовать логическое значение в условиях? Правильно! Если результатом условия будет любое ненулевое значение, то выполняться будет стейтмент1 .
Поэтому, если попробовать сделать что-то вроде следующего:
То результатом будет hi , так как 4 является ненулевым значением.
Что такое простое число? Правильно! Это целое положительное число больше единицы, которое делится без остатка либо на себя, либо на единицу. Напишите программу, которая просит пользователя ввести простое целое число, меньшее 10. Если пользователь ввел одно из следующих чисел: 2, 3, 5 или 7 — программа должна вывести The digit is prime , в противном случае — The digit is not prime .
Подсказка: Используйте ветвление if для сравнения чисел и логические значения для отслеживания того, является ли пользовательское число простым или нет.
Я как-то никогда не задумывался над тем, что лучше использовать BOOL или BOOLEAN? Конечно же, BOOL — это и короче и во всех учебниках по Windows встречается именно BOOL. Как бы не так! Буквально вчера я битый час занимался поиском ошибки там, где ее не должно было быть.
Оказалось, что единственно истинный тип, впрямую связанный с типом bool, который определен стандартами языка С++, это именно BOOLEAN. А BOOL это не что иное, как «typedef int BOOL;» и находится в windows.h (точнее в WinDef.h, но это неважно)
Рассмотрим подробнее исходный код функции, сравнивающий два числа:После компилирования Visual Studio и запуска, имеем: Equals
Тогда поменяем BOOL на BOOLEAN:
Компилируем, запускаем, получаем: Not equals (что и должно было получиться с самого начала)
Вывод: никогда не пользуйтесь BOOL, только BOOLEAN.
UPD1: Поправил в возврате функции, было TRUE/FALSE стало true/false для чистоты эксперимента.
UPD2: Раскрываю «черную магию». Возврат bool идет в типе char (регистр CPU al), так как bool в Visual Studio приравнен к char (и BOOLEAN там тоже приравнен к char поэтому замена BOOL на BOOLEAN убирает ошибку).
А вот тип BOOL приравнен к int (регистр eax), поэтому, когда функция возвращает false (он же FALSE), то при этом в нуль устанавливается только младший байт al, а старшие байты (ah и прочий eax) — там будет ненулевой мусор, на который BOOL, вобравший в себя результат bool, среагирует как на eax!=0 и возникнет ошибка.
Мы должны были бы внутри последнего if перейти на ветку с eax==0 (Not equals), а перешли на ветку с eax!=0 (Equals), потому что функция CompareInt вернула нам только al равным 0, а старшие (мусорные) байты в eax внутри функции CompareInt при этом (ошибочно?) не были установлены в 0.
UPD3: Кстати, скомпилировал этот код древним (2006 г.) Borland Builder C++, все биты eax при возврате false внутри функции CompareInt явно устанавливаются в 0 путем xor eax,eax — поэтому ошибки нет.
Читайте также: