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

Философия программирования на C++ Выпуск 21. Типы данных


Добрый день!

Сегодня мы рассмотрим примеры использования различных типов данных из прошлого выпуска. Все постараемся рассмотреть в одном абстрактном примере.

Попробуем считать числа посимвольно и выполнить то же, что и в прошлой программе - сложить, вычесть, поделить или умножить два числа! Единственное отличие - вводить числа мы будем через char.

#include <iostream>
using namespace std;
int main() {
    int number = 0;  // Число - на этот раз целое
    cout << "Please, enter expression: ";
    double result = 0;  // Сразу будем записывать результат!
    char operation = 0;  // Здесь будет последняя введенная операция
    char c = 0;
    cin >> c;
    while(true) {  // Будем вводить числа до бесконечности, выход из цикла будет внутри
        // Символы можно сравнивать, поскольку они по сути являются числами
        // Каждому числу соответствует свой символ в таблице ASCII (см. прошлый выпуск)
        if (c <= '9' && c >= '0') {  // Если введенный символ - цифра (между символом 0 и 9)
            // Тогда прибавляем эту цифру к вводимому числу (умножим его на десять)
            int add = c - '0';  // Да-да, если мы вычтем из введенного символа символ '0', то и получим введенную цифру
            number = number*10 + add;  // Прибавляем очередной десяток к уже введенному числу
        } else if (c == '/' || c == '*' || c == '-' || c == '+' || c == '=') {
            // Введена одна из операций - значит нужно применить предыдущую операцию с новым числом
            // operation хранит последнюю введенную операцию
            if (operation == '/') {
                if (number == 0) {  // Не забываем проверять деление на ноль!
                    cout << "Cannot divide by 0!" << endl;
                    return 0;
                }
                result /= number;
            }
            if (operation == '*')
                result *= number;
            if (operation == '+')
                result += number;
            if (operation == '-')
                result -= number;
            if (operation == 0)  // Еще не было никаких введенных операций! Сохраняем просто число
                result = number;
            number = 0;  // Обнуляем число, его мы начнем вводить заново
            if (c == '=')  // Если пользователь ввел равенство, то выходим из цикла и выводим результат
                break;
            operation = c;  // Сохраняем очередную операцию
        } else {
            // Введен недопустимый символ
            cout << "Incorrect symbol: " << c << endl;
            return 0;  // Выходим из программы
        }
        cin >> c;  // Не забываем ввести очередной символ
    }
    cout << "Result: " << result << endl;
}

 Надеюсь, что в программе достаточно хорошо все описано, я лишь приведу пару пояснений про символьный тип. Каждому символу, как мы уже говорили, соответствует некоторое число. В таблице они расположены так, чтобы подряд идущие символы в алфавите шли подряд и в таблице. То же самое и с цифрами. То есть для проверки, что введенный символ является цифрой - достаточно посмотреть, что он находится между символами '0' и '9'. А для проверки, что введенный символ - большая буква, достаточно проверить, что он находится между 'A' и 'Z' (да, большие и маленькие символы имеют разные коды). Правда, с русским алфавитом это не всегда так.

Таким образом, вам даже необязательно знать коды этих символов (хотя это и бывает иногда полезным).

Теперь приведем абстрактный пример для понимания сути остальных типов данных:

#include <iostream>
using namespace std;
int main() {
    cout.precision(40);  // Будем выводить до 40 значащих цифр
    int a = 1000000;  // Обычная переменная int - то же самое, что и long int
    // Оператор sizeof возвращает размер переменной в байтах
    cout << "a: " << a << ", size: " << sizeof(a) << endl;
    short b = 1000000;  // Укороченная переменная (то же самое, что и short int)
    cout << "b: " << b << ", size: " << sizeof(b) << endl;
    unsigned c = -1;  // unsigned int - беззнаковое число
    cout << "c: " << c << ", size: " << sizeof(c) << endl;
    unsigned long long int d = a*a*a;  // Да-да, этот тип может хранить настолько большие значения (до 18 знаков!)
    cout << "d: " << d << ", size: " << sizeof(d) << endl;
    float e = 1234567890.;  // Пытаемся сохранить большое число в вещественный тип
    cout << "e: " << e << ", size: " << sizeof(e) << endl;
    double f = 1234567890.1234567890;  // А сюда добавим еще и вещественную часть
    cout << "f: " << f << ", size: " << sizeof(f) << endl;
    long double g = 1.;  // С введением этого числа возникли сложности...
    g += 1e-19;  // Может хранить до 19-20 значащих цифр
    for (int i = 0; i < 100; i++)  // Умножим на 1e4000
        g *= 1e40;
    cout << "g: " << g << ", size: " << sizeof(g) << endl;
}

Здесь можно увидеть, что все типы имеют свой размер, свою точность и максимальное значение. Тип long double имеет размер 16 байт и вмещает до 19 знаков.

А float смог уместить только 7 значащих цифр (остальные числа уже не совпадают).

Казалось бы, что можно же всегда использовать один тип с максимальными интервалами. Но на самом деле это не так, поскольку часто приходится экономить память компьютера (а она не бесконечная) в больших программах, да и как правило такой острой необходимости в таких больших числах практически никогда не бывает. Поэтому чаще всего и используют int, unsigned int (да, он не умеет хранить знак минус), float или double.

Также стоит отметить, что переменные могут переполниться. То есть когда мы пытаемся им присвоить значения больше, чем они могут уместить, то старшая часть просто отбрасывается. Какая именно? Об этом в следующем выпуске, где мы рассмотрим более подробно, как же именно хранится число в компьютере.

До следующего выпуска! Спасибо, что остаетесь с нами! 


В избранное