Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Философия программирования на C++: условия, часть третья и последняя


Условия, часть третья

Приветствую всех подписчиков нашей рассылки. В этом выпуске речь пойдет об условных выражениях и об автоматическом преобразовании типов компилятором.

Будем идти последовательно. Сначала изучим ключевые слова bool, true, false. Для более простого понимания желательно знать основы булевой алгебры. Хотя написав пару программ и поизучав результаты в принципе можно все понять самостоятельно. Во всяком случае для экономии места дальше мы будем исходить из того, что вам известны логические операции И, ИЛИ, НЕ, исключающее ИЛИ. В рассылке уже упоминалось все это в первой части условий.
Раньше программистам приходилось прибегать к различным приемам для работы с логическими значениями, от этого возникали различные ошибки и была плохая переносимость программ. Сейчас в стандарт С++ включены следующие ключевые слова: тип данных bool, которое имеет два возможных состояния (логических значений), представленных встроенными константами true (целое число 1) и false (целое число 0). Сразу приведем маленький пример. Раз bool - тип данных (наряду с int, float), то можно создавать переменные данного типа.
#include "iostream"
using namespace std;
int main() {
    bool t = true;
    bool f = false;
    cout << "t = " << t << endl;
    cout << "f = " << f << endl;
    cout << "Size of bool: " << sizeof(t) << endl;
}

Хотя занимаемый размер типа bool в памяти 1 байт (256 значений), никаких других значений, кроме 0 и 1, переменные типа bool не могут принимать. Но компилятор автоматически может приводить значение типа int к bool, интерпретируя все ненулевые значения как true, а нулевое как false. Правда в идеале компилятор должен выдавать предупреждение с рекомендацией внести изменения. Возможно и обратное преобразование от bool к int. То есть компилятор будет интерпретировать значение типа bool как целое 0 или 1. Пример.
#include "iostream"
using namespace std;
int main() {
    bool t = true;
    bool f = false;
    int i = -10;
    bool bi = i;
    int ibi = bi;
    cout << "bi = " << bi << endl;
    cout << "ibi = " << ibi << endl;
    cout << "f+t = " << f+t << endl;
    cout << "f-t = " << f-t << endl;
}

Мой компилятор выдал предупреждение на строчку bool bi = i, ваш скорее всего сделает так же. Напомню, что про приведение типов мы говорили в одном из прошлых выпусков, когда говорили про типы данных. В приведенном примере стоит отметить результат последнего вычитания. Будет выведено -1, что в принципе логично. Дело в том, что для типа данных bool не существует операции вычитания, поэтому компилятор приводит f и t к типу int, в итоге получаем тоже тип int. Поэтому довольно удобно использовать эту особенность для сокращения строк кода за счет неиспользования оператора if (об этом чуть ниже).
Над типом bool возможны следующие операции: И(&&), ИЛИ(||), НЕ(!). Это основные логические операции (в скобках указаны операторы С++, реализующие данные операции). С их помощью можно выразить любую булеву функцию (система является полной), например с помощью представления в виде СКНФ (ну это дело математики, которую хорошему программисту лучше знать). Ниже показаны возможные применения эти операций.
#include "iostream"
using namespace std;
int main() {
    bool t = true;
    bool f = true;
    cout << "t&&f = " << (t&&f) << endl;
    cout << "t||f = " << (t||f) << endl;
    cout << "!t = " << (!t) << endl;
    cout << "t xor f = " << ((t && !f) || (!t && f)) << endl;    //Записали в виде СДНФ
}

Нам здесь пришлось использовать скобки, поскольку оператор << имеет больший приоритет над логическими операциями. Указывая скобки мы ставим выражение внутри их по приоритету выше. Все как в математике. Заметим, что оператор отрициния ! имеет больший приоритет, чем операторы && и ||. Поэтому в последнем выражении (где исключающее ИЛИ записано в виде СДНФ) !f и !t в скобки не взяты.
Очевидно, что результатом операторов &&, ||, ! является тип bool. Кроме этих операторов, существуют и другие, результатом которых является этот тип данных. Например, > (больше), < (меньше), == (равно), >= (больше или равно), <= (меньше или равно), != (не равно). Мы использовали их в предыдущих выпусках в условиях. Теперь можно сказать, что в условном операторе в качестве усовия фактически используется некое значение bool. Ниже приведен короткий пример, демонстрирующий использование переменных типа bool в условиях, а также операции, приводящие к типу bool.
#include "iostream"
using namespace std;
int main() {
    bool t = true;
    bool f = true;
    if(t)
        cout << "t = true" << endl;
    else
        cout << "t = false" << endl;
    int a = 10;
    int b = 20;
    if(a == b)
        cout << "a == b" << endl;
    else
        cout << "a != b" << endl;
    f = (a == b);    //Результат сравнения - bool
    if(f)
        cout << "a == b" << endl;
    else
        cout << "a != b" << endl;
}

Ну тут должно быть все понятно. В строке f = (a == b) переменной f присваивается результат сравнивания a и b (true, если a равно b, false в противном случае).
А теперь я обращу ваше внимание на второе условие a==b в операторе if. Как отмечалось выше, тип bool автоматически приводится к типу int, и наоборот. Поэтому если вы ошибетесь и напишите if(a=b), то результат будет совершенно не тот. Дело в том, что результатом присваивания является значение переменной слева (оператор = возвращает значение). Поэтому сначала переменной a присваивается значение b (20), а потом это значение приводится к типу bool (20 переходит в true), в результате условие выполняется и выводится "a == b", что не является верным. Это одна из опасных и частых ошибок (впрочем компиляторы часто выдают предупреждения, поэтому сейчас это не так уж страшно, как было пару лет назад). Из такого поведения оператора присваивания можно извлекать пользу. Ниже приведен маленький пример использования этой особенности и его использование в условии. Кроме того приведены и примеры использования преобразования типа bool к типу int.
#include "iostream"
using namespace std;
int main() {
    bool t = true;
    bool f = true;
    int a = 0;
    int b = 20;
    int c = 10;
    a = b = c;    //После этого и a, и b, и с будут равны 10.
    if(a == b)
        cout << "a == b" << endl;
    else
        cout << "a != b" << endl;
    b = 100;    //Присваиваем b другое значение
    if(a = b)    //!Ошибочное использование, тут a не равно b
        cout << "a == b" << endl;
    else
        cout << "a != b" << endl;
    a = 40 + (b = c);    //Можно использовать и так :)
    f = t-1;    //f станет false
    a = (!f) * 20;    //Значение a будет равно 20
    c = (a > b) + c;    //Результатом будет c+1, то есть 11.
}

Разберем последние 4 строчки. В первой из них переменной a присваивается значение 40 плюс результат присваивания переменной b значения переменной c. Сначала выполняется b = c (b становится равным 10, значению c). После этого оператор = возвращает это значение (10), к нему прибавляет 40, в результате получим 50. В следующей строке f становится равным false (поскольку t равно 1, вычитая 1 получим 0, что приводится к типу bool как значение false). Слудеющиую строку можно было записать и так:
if(!f)
    a = 20;
else
    a = 0;

Очевидно, что написать a = (!f) * 20 проще, хотя и чуть менее понятно. Ну в последней строчке вы и сами должны разобраться.
Ну и для дополнения расскажем об операторе ?:, который является единственным тернарным оператором в С++ (принимает 3 операнда). Его вид такой:
(условие) ? (выражение1) : (выражение2);
В условие подставляется значение типа bool, а выражения должны возвращать значение (одного и того же типа). Если условие верно (true), то возвращается результат выражения 1, в противном случае результат выражения 2.
В этом случае условие выше можно было бы записать так:
a = f ? 0 : 20;
Если f равно true, то переменной a присваивается 0, в противном случае 20. Этот оператор в основном используется для уменьшения строчек кода.

Вот вроде бы и все, что достаточно знать об условиях и булевых переменных (типа bool, что является сокращением от boolean).
На этом все, все свои вопросы вы всегда можете присылать на почтовый ящик asm_89@mail.ru .

В избранное