Типы данных. Переполнение.

В предыдущем уроке мы изучили основные типы целочисленных данных на языке С++, узнали, как создавать переменные соответствующего типа и, какие числа можно хранить в этих переменных.

Однако, иногда возникают ситуации, когда полученное в результате математической операции число, больше, чем может содержать переменная определенного типа. Например, для типа 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.

tc_005

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

tc_006

Эта программа демонстрирует возможности использования переменных нескольких типов. Разберем эту программу построчно:

#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!

tc_007

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

tc_008

При нажатии на любую клавишу программа закроется.

Для каждого типа мы видим два результата: первый результат имеет правильное значение, а второй нет, поскольку произошло переполнение.

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