Типы данных. Переполнение.
В предыдущем уроке мы изучили основные типы целочисленных данных на языке С++, узнали, как создавать переменные соответствующего типа и, какие числа можно хранить в этих переменных.
Однако, иногда возникают ситуации, когда полученное в результате математической операции число, больше, чем может содержать переменная определенного типа. Например, для типа unsigned char максимально допустимое число – 255. Если мы к этому числу прибавим 1, то, казалось бы, должно получиться 256, но на самом деле получается 0. Почему так происходит? Сложим столбиком в двоичной форме числа 11111111 (255) и 1:

Как считать столбиком в двоичной форме? Точно также, как и в десятичной, только нужно помнить, что 1 + 1 =10, а 1 + 1 + 1 = 11. Начинаем сложение с самых правых чисел: 1 + 1 = 10, 0 пишем, а 1 запоминаем (эта единица отмечена красным). Ко второй справа единице добавляем 1, которую мы запомнили: получается 10, 0 пишем, 1 запоминаем и т.д… К последней единице (та, что слева) добавляем 1, которую мы запомнили на предыдущем шаге. Получаем 10. Это число записываем снизу. В результате получилось число 100000000, равное 256, если рассматривать его в десятичной форме. Но в типе unsigned char используется только 1 байт, состоящий из 8-ми двоичных разрядов (бит). В результате в этот байт помещаются 8 полученных нулей (они обведены красным прямоугольником), а девятый разряд 1 не помещается в нашу переменную. Число 00000000 в десятичной форме равно 0. В результате мы получили, что 255 + 1 = 0.
Рассмотрим другой пример сложим числа типа unsigned char 250 и 10 (в двоичной форме 11111010 и 1010 соответственно):

В результате получилось число 100000100, которое соответствует десятичному числу 260. Однако, старшая (девятая) единица снова не поместилась в один байт. Поэтому в переменной осталось число 00000100, равное 4 в десятичной форме. Итак, 250 + 10 = 4.
На самом деле такой результат не является ошибкой. Ни среда разработки, ни программа, ни операционная система никак не отреагируют на неверный результат, поскольку переполнение является обычной ситуацией в процессе работы центрального процессора или контроллера. За эту ошибку ответственен только программист, который неверно подобрал тип переменной. Поэтому необходимо внимательно подходить к выбору типа.
Скачайте следующую программу, написанную на языке C++, поместите скачанный файл TYPES02.CPP в папку C:\TCPP\BIN\.
Запустите DOSBox (Как настроить эту программу можно посмотреть ЗДЕСЬ ).
Нажмите клавишу F3. Выберите файл TYPES.CPP и нажмите клавишу Enter.

На экране должна появиться открытая программа TYPES02.CPP:

Эта программа демонстрирует возможности использования переменных нескольких типов. Разберем эту программу построчно:
- #include<iostream.h> - подключаем модуль iostream.h. В этом модуле содержится функция cout, которая используется в этой программе ниже. Она необходима для вывода информации на экран.
- #include<conio.h> - подключаем модуль conio.h, содержащий функции clrscr() и getch();
- void main(){- описываем заголовок функции main. Это главная функция. Она должна быть в каждой программе на языке С++. void – это тип функции main. Void – означает, что наша функция ничего не возвращает. Пустые скобки () означают, что у этой функции нет входных параметров. Любая функция начинается c открывающейся фигурной скобки {, а заканчивается закрывающейся фигурной скобкой }.
- clrscr(); - функция, которая очищает экран.
- unsigned char a1,b1,c1; - объявляем три переменные – a1, b1 и c1 типа unsigned char.
- cout<<"unsigned char:\n"; - выводим на экран надпись «unsigned char:». Обратите внимание на специальную комбинацию символов «\n». Когда программа встречает эту комбинацию при выводе текста на экран, она переводит курсор на новую строчку.
- a1=100; - переменной a1 присваиваем значение 100.
- b1=15; - переменной b1 присваиваем значение 15.
- c1=a1+b1; - переменной c1 присваиваем сумму значение переменных a1 и b1.
- cout<<int(a1)<<" + "<<int(b1)<<" = "<<int(c1)<<"\n"; - выводим на экран сначала значение переменной a1, затем текст « + », затем значение переменной b1, потом текст « = », затем значение переменной с1, и в конце выводим текст «\n», что означает переход курсора на следующую строчку. Обратите внимание, что в этой функции мы не просто пишем имя переменной, а предварительно преобразуем ее тип из unsigned char в int с помощью функции int(). Это нужно для того, чтобы числа типа unsigned char или char корректно отображались на экране. Если не преобразовать тип переменной в тип int, то функция cout посчитает, что мы хотим вывести не числа, а символы, которые соответствуют этим числам в таблице символов ACSII (об этом мы поговорим в другой раз).
- a1=255; - переменной a1 присваиваем значение 255.
- b1=1; - переменной b1 присваиваем значение 1.
- c1=a1+b1; - переменной c1 присваиваем сумму значение переменных a1 и b1.
- cout<<int(a1)<<" + "<<int(b1)<<" = "<<int(c1)<<"\n"; - выводим на экран информацию. Аналогично описанному выше.
- unsigned int a2,b2,c2; - объявляем три переменные – a2, b2 и c2 типа unsigned int.
- cout<<"\nunsigned int:\n"; - переводим курсор на следующую строчку («\n» в начале текста), выводим на экран надпись «unsigned int:» и переводим курсор на следующую строчку («\n»).
- a2=50000; - переменной a2 присваиваем значение 50000.
- b2=153; - переменной b2 присваиваем значение 153.
- c2=a2+b2; - переменной c2 присваиваем сумму значение переменных a2 и b2.
- cout<<a2<<" + "<<b2<<" = "<<c2<<"\n"; - вывод результата на экран, как описано выше. Поскольку теперь переменные имеют тип unsigned int, для них уже не нужно использовать функцию преобразования типа int();
- a2=65530; - переменной a2 присваиваем значение 65530.
- b2=15; - переменной b2 присваиваем значение 15.
- c2=a2+b2;- переменной c2 присваиваем сумму значение переменных a2 и b2
- cout<<a2<<" + "<<b2<<" = "<<c2<<"\n"; - Выводим на экран результат.
- int a3,b3,c3; - объявляем три переменные – a3, b3 и c3 типа int.
- cout<<"\nint:\n"; - переводим курсор на следующую строчку («\n» в начале текста), выводим на экран надпись «int:» и переводим курсор на следующую строчку («\n»). Далее все аналогично предыдущим примерам.
- a3=15000;
- b3=25;
- c3=a3+b3;
- cout<<a3<<" + "<<b3<<" = "<<c3<<"\n";
- a3=32760;
- b3=10000;
- c3=a3+b3;
- cout<<a3<<" + "<<b3<<" = "<<c3<<"\n";
- getch(); - это функция ждет нажатия клавиши. Если не использовать эту функцию, то программа после вывода результата просто закроется, но благодаря функции getch() программа не закроется, пока не будет нажата любая клавиша.
- } – конец функции main.
Чтобы запустить программу, нажимаем комбинацию клавиш Alt+R, выбираем в появившемся меню пункт Run и нажимаем клавишу Enter. Ни в коем случае не нажимайте для запуска программы комбинацию клавиш Ctrl+F9 – это закроет DOSBox!

На экране появится результат работы программы:

При нажатии на любую клавишу программа закроется.
Для каждого типа мы видим два результата: первый результат имеет правильное значение, а второй нет, поскольку произошло переполнение.
Задание: 1) Какой будет результат сложения переменных a1 и b1 типа unsigned char, если a1=200, а b1=100? Внесите эти данные в программу, запустите ее и посмотрите результат. 2) Присвойте переменным свои значения и посмотрите результат. 3) вместо сложения, попробуйте другие операции: вычитание (-), умножение(*), деление (/).