Урок №31. Целочисленные типы данных: short, int и long

В данном занятии мы изучим целочисленные типы данных в языке программирования C++, их диапазоны значений, операцию деления, а также примеры переполнения (определение и иллюстрации).
Целочисленные типы данных
Целочисленный тип данных представляет собой категорию типов, в которых переменные могут хранить только целые числа без дробной части, такие как -2, -1, 0, 1, 2. В языке программирования C++ существует пять основных целочисленных типов, которые можно использовать:

Примечание : Тип char — это особый случай: он является как целочисленным, так и символьным типом данных. Об этом детально мы поговорим на одном из следующих уроков.
Главное отличие между перечисленными выше целочисленными типами заключается в их вместимости: чем больше размер, тем больше значений может содержать переменная данного типа.
Определение целочисленных переменных
Определение осуществляется в следующей манере:
char c ;
short int si ; // допустимо
short s ; // предпочтительнее
int i ;
long int li ; // допустимо
long l ; // предпочтительнее
long long int lli ; // допустимо
long long ll ; // предпочтительнее
Хотя можно использовать полные названия short int, long int и long long int, предпочтительнее использовать их сокращенные версии (без int). Кроме того, постоянное повторение int затрудняет понимание кода (легко перепутать с именем переменной).
Диапазоны значений и знак целочисленных типов данных
Как уже было упомянуто в предыдущем уроке, переменная с определенным количеством бит может содержать 2n различных значений. Но какие именно значения это могут быть? Это значения, которые находятся в определенном диапазоне. Диапазон - это интервал значений, которые может хранить определенный тип данных. Диапазон целочисленной переменной зависит от двух факторов: ее размера (измеряемого в битах) и ее знака (который может быть signed или unsigned).
Тип signed (со знаком) в языке программирования означает, что переменная может хранить как положительные, так и отрицательные целые числа. Для объявления переменной как signed, необходимо использовать ключевое слово signed:
signed char c ;
signed short s ;
signed int i ;
signed long l ;
signed long long ll ;
По умолчанию, перед типом данных обычно указывается ключевое слово signed.
Переменная целого типа со знаком, занимающая 1 байт, может принимать значения от -128 до 127. Это означает, что любое число в этом диапазоне может быть сохранено в ней безопасно.
Иногда мы можем заранее предположить, что в программе не будет использоваться отрицательные числа. Это часто бывает, когда переменные используются для хранения количества или размера объектов (например, рост или вес не могут быть отрицательными).
Тип данных unsigned предназначен для хранения только положительных целых чисел. Для объявления переменной с таким типом данных используйте ключевое слово unsigned:
unsigned char c ;
unsigned short s ;
unsigned int i ;
unsigned long l ;
unsigned long long ll ;
Переменная без знака типа unsigned, занимающая 1 байт, может принимать значения от 0 до 255.
Важно помнить, что при объявлении переменной как unsigned она не может содержать отрицательные числа, а только положительные.
После того, как вы осознали различие между signed и unsigned, давайте изучим диапазоны значений различных типов данных:

Для специалистов в области математики: Переменная signed с n-битовым размером имеет диапазон значений от -(2n-1) до 2n-1 - 1. Переменная unsigned с n-битовым размером имеет диапазон значений от 0 до (2n) - 1.
Для тех, кто не знаком с математикой: Применяем таблицу
Иногда у начинающих программистов возникают затруднения с различием между signed и unsigned переменными. Однако есть простой способ запомнить их различия. Чем отличается отрицательное число от положительного? Правильно! Минусом спереди. Если минуса нет, то число является положительным. Следовательно, целочисленный тип со знаком (signed) означает, что минус может присутствовать, что позволяет числам быть как положительными, так и отрицательными. Целочисленный тип без знака (unsigned) означает, что минус спереди отсутствует, что позволяет числам быть только положительными.
Что используется по умолчанию: signed или unsigned?
Что случится, если мы объявим переменную без указания знакового или беззнакового типа данных?

По умолчанию все целочисленные типы данных, за исключением char, являются знаковыми. Тип char может быть как знаковым, так и беззнаковым (хотя обычно он знаковый).
Обычно слово signed не указывается (так как оно автоматически используется).
Обычно программисты стараются избегать использования беззнаковых целочисленных типов, если это необходимо, потому что статистика показывает, что ошибки возникают чаще с переменными беззнакового типа, чем с переменными знакового типа.
Рекомендация: Предпочтительнее использовать знаковые целочисленные типы данных вместо беззнаковых.
Переполнение
Вопрос: "Что случится, если мы попытаемся использовать значение, которое выходит за пределы допустимого диапазона определенного типа данных?". Ответ: "Произойдет переполнение".
Переполнение (или "overflow" на английском) возникает, когда переменная теряет биты из-за недостаточного выделения памяти для их хранения.
На последнем занятии мы обсуждали, что информация сохраняется в двоичном формате, где каждый бит может принимать только два значения (0 или 1). Вот как представлен диапазон чисел от 0 до 15 в десятичной и двоичной системах:

Чем больше число, тем больше битов ему требуется, как видно из приведенного выше. Из-за того, что наши переменные имеют ограниченный размер, существуют ограничения на количество данных, которые они могут содержать.
Примеры переполнения
Давайте изучим переменную беззнакового типа, которая содержит 4 бита. Любое из представленных в таблице выше двоичных чисел может быть помещено в эту переменную.
Что случится, если мы попытаемся присвоить значение, которое занимает больше 4 бит? Правильно! Произойдет переполнение. Наша переменная сохранит только 4 наименее значимых бита (те, что справа), а все остальные будут потеряны.
Допустим, если мы попытаемся сохранить значение 21 в нашей переменной, которая занимает 4 бита:

Пятерка занимает 5 бит (10101). Последние 4 бита (0101) будут сохранены в переменной, а первый бит (1) будет проигнорирован. Таким образом, переменная будет содержать 0101, что эквивалентно 101 (нули в начале не учитываются), что соответствует числу 5, а не 21.
Примечание : О конвертации чисел из двоичной системы в десятичную и наоборот будет отдельный урок, где мы всё детально рассмотрим и обсудим.
Давайте рассмотрим пример в коде, где тип short занимает 16 бит:
#include
int main ( )
{
unsigned short x = 65535 ; // наибольшее значение, которое может хранить 16-битная unsigned переменная
std :: cout << "x was: " << x << std :: endl ;
x = x + 1 ; // 65536 - это число больше максимально допустимого числа из диапазона допустимых значений. Следовательно, произойдет переполнение, так как переменнная x не может хранить 17 бит
std :: cout << "x is now: " << x << std :: endl ;
return 0 ;
}
Результат работы программы:
x was: 65535
x is now: 0
Что произошло? Возникла ситуация переполнения, так как мы пытались присвоить переменной x значение, которое превышает её допустимую вместимость.
Для тех, кто хочет знать больше: Число 65 535 в двоичной системе счисления представлено как 1111 1111 1111 1111. 65 535 — это наибольшее число, которое может хранить 2-байтовая (16 бит) целочисленная переменная без знака, так как это число использует все 16 бит. Когда мы добавляем 1, то получаем число 65 536. Число 65 536 представлено в двоичной системе как 1 0000 0000 0000 0000, и занимает 17 бит! Следовательно, самый главный бит (которым является 1) теряется, а все 16 бит справа — остаются. Комбинация 0000 0000 0000 0000 соответствует десятичному 0, что и является нашим результатом.
Таким же образом, возникнет переполнение, если мы выберем число, которое находится ниже минимального значения в заданном диапазоне:
#include
int main ( )
{
unsigned short x = 0 ; // наименьшее значение, которое 2-байтовая unsigned переменная может хранить
std :: cout << "x was: " << x << std :: endl ;
x = x - 1 ; // переполнение!
std :: cout << "x is now: " << x << std :: endl ;
return 0 ;
}
Результат работы программы:
x was: 0
x is now: 65535
Избыточное заполнение может привести к утрате данных, что недопустимо. Если есть хоть малейшее подозрение или предположение, что значение переменной может выходить за пределы допустимого диапазона для данного типа данных, лучше выбрать более крупный тип данных!
Правило: Никогда не допускайте возникновения переполнения в ваших программах!
Деление целочисленных переменных
При делении двух целых чисел в языке программирования C++, где результат также является целым числом, все происходит вполне предсказуемо:
#include
int main ( )
{
std :: cout << 20 / 4 << std :: endl ;
return 0 ;
}
Итак, вот что у нас получилось:
5
Что случится, если при делении двух целых чисел мы получим десятичную дробь? Например:
#include
int main ( )
{
std :: cout << 8 / 5 << std :: endl ;
return 0 ;
}
Результат:
1
При делении целых чисел в языке C++ всегда получается другое целое число, без дробной части (она просто отбрасывается, а не округляется).
Давайте внимательно изучим приведенный выше пример: 8 / 5 = 1.6 . Однако, как мы знаем, при делении целых чисел результатом будет целое число. Поэтому дробная часть (0.6 ) будет отброшена, и останется только 1 .
Правило: Будьте осторожны при делении целых чисел, так как любая дробная часть всегда отбрасывается.
Comments