§ 8.5. Целые типы. Преобразование типов. Сокращенные операции. Операции инкремента и декремента

Школьный курс python
Содержание

Общие сведения о целых типах

Целочисленный тип данных является одним из фундаментальных типов и относится (наряду с вещественными) к арифметическим типам. Он представлен в виде знаковых и беззнаковых разновидностей типов.
С помощью модификаторов типа можно получить всё разнообразие целых типов (перечисленных в таблице 1). Модификаторы типа могут определять знак и/или размер. Знак определяют модификаторы:

  • signed
  • unsigned
Для определения размера (или ширины) используются модификаторы:
  • short
  • long
  • long long
Сочетание модификаторов определяет спецификатор целочисленного типа.

Таблица 1. Спецификаторы целых типов
Size MIN MAX
short
2 -32768 32767
unsigned short
2 0 65535
int
4 -2147483648 2147483647
unsigned int
4 0 4294967295
long
8 -9223372036854775808 9223372036854775807
unsigned long
8 0 18446744073709551615
long long
8 -9223372036854775808 9223372036854775807
unsigned long long
8 0 18446744073709551615

Модификатор signed используется по умолчанию, поэтому его можно опустить (т. е. int на самом деле signed int). Спецификатор int также можно опустить во всех типах, в которых используется один из модификаторов определяющих знак или ширину. Поэтому, на практике, сокращение int используется только для типа signed int.

Для вывода таблицы размеров максимального и минимального значений целых типов использовался заголовок limits и операция sizeof(). Константы numeric_limits<type>::min() и numeric_limits<type>::max() хранят максимальные и минимальные значения соответствующих типов.

Как видно из таблицы, знаковые типы могут принимать как отрицательные, так и положительные значения, в то время как беззнаковые типы только положительные и ноль. В стандарте C++ не определяются размеры того или иного типа. Стандарт дает лишь гарантию, что:

1 == sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long)

Размер конкретного типа будет зависеть от платформы и реализации C++. Компилятор GCC не делает разницы между long и long long (а также unsigned long и unsigned long long), но, для переносимости программ, это соответствие целых типов следует учитывать.
Типом по умолчанию для целых типов является int. Ширина типа int гарантированно имеет значение 32 бита во всех архитектурах.
Задача 1. Составить программу в которой переменные целого типа обмениваются своими значениями без использования третьей переменной.

Программа 8.5.1
#include <iostream>
using namespace std;

int main() {
    int a, b;
    cout << "a = "; cin >> a;
    cout << "b = "; cin >> b;
    a = a + b;
    b = a - b;
    a = a - b;
    cout << "a = " << a << "\n"
		 << "b = " << b << endl;
    return 0;
}

Литералы целых типов представлены в двоичной, восьмеричной, шестнадцатеричной и десятичной системах счисления:

  • Десятичные (без префикса): -10, 1'000'000'000
  • Двоичные (префикс 0b или 0B): 0b1010, 0B111100111, 0b1'1011'0110'1111
  • Восьмеричные (префикс 0): 01, 020, 07155
  • Шестнадцатеричные (префикс 0x или 0X): 0xA, 0x1B8, 0X00FF
Для литералов любой системы счисления можно использовать суффиксы подсказывающие компилятору какой подразумевается тип данных (суффиксы не чувствительны к регистру):
  • uunsigned int;
  • llong;
  • ulunsigned long;
  • lllong long;
  • ullunsigned long long.
Представление в системах счисления по основанию 8, 10 и 16 на выводе осуществляется с помощью манипуляторов oct, dec и hex, соответственно.

Целочисленные типы определяют лишь небольшое множество соответствующих значений из бесконечной совокупности целых чисел. Чем больше количество различных значений, которые могут принимать переменные данного типа, тем шире используемый тип.
Стандартных средств контроля переполнения типа C++ не имеет! Вся ответственность за соблюдения выхода за границы ширины типа целиком лежит на разработчике алгоритма.

Тем не менее, создатели GCC учли сей факт и разработали функции контроля за соблюдением границ ширины типа. С этими функциями мы познакомся позднее.

Максимально большое целое число можно получить с помощью типа unsigned long long. Однако, есть задачи, в которых требуется еще большее количество десятичных разрядов. В таком случае, как мы уже упоминали в предыдущем уроке, нужно переходить к длинной арифметике с массивами.
На прошлом уроке мы познакомились с библиотекой Boost.Multiprecision. Эта библиотека предоставляет типы для работы с длинными целыми числами. Для ускорения работы программы предусмотрен механизм определения длины контейнера для хранения "длинного" числа до его использования. Если выделенная память будет исчерпана, то увеличение произойдет автоматически. Приведем небольшой пример для иллюстрации.

Программа 8.5.2
#include <boost/multiprecision/cpp_int.hpp>	 	 
#include <iostream>
using namespace boost::multiprecision;	 	 
using namespace std;	 	 
int main() {	 	 
	uint256_t num1, num2; // max 115792089237316195423570985008687907853269984665640564039457584007913129639935	 	 
	cout << "num1 = "; cin >> num1;	 	 
	cout << "num2 = "; cin >> num2;	 	 
	cout << "num1 + num2 = " << num1 + num2 << endl;	 	 
 return 0;	 	 
}	 	 
num1 = 555555555555555555555555555555555555555555555	 	 
num2 = 555555555555555555555555555555555555555555555	 	 
num1 + num2 = 1111111111111111111111111111111111111111111110	 	 

size_t

В стандартной библиотеке определен еще один беззнаковый целочисленный тип - std::size_t (не виден вне std). Объект этого типа может хранить максимально возможный размер объекта любого типа (включая массив). size_t обычно используется для индексации массива и для подсчета в циклах. Тип size_t возвращают операции:

  • sizeof
  • sizeof ...
  • alignof
функция стандартной библиотеки std::size() и другие функции.

Преобразования типов

Мы уже показывали, что в выражении могут участвовать разные типы. С++ дает возможность преобразовать объекты одного типа в объекты другого типа. Различают два вида преобразования:

  • Неявные преобразования
  • Явное преобразование
Различие между ними в том, что неявные преобразования производятся автоматически, без участия программиста. К неявным преобразованиям (implicit conversion) относят:
  • Преобразования при инициализации и присваивании
  • Преобразования при передаче аргументов
  • Арифметические преобразования (arithmetic conversion)
Преобразования при инициализации и присваивании

Здесь для нас важно не сами правила преобразования, а те требования, которые нужно соблюдать для сохранения точности вычислений:

  • чтобы получить предсказуемый результат (без потери точности) необходимо типу с большим диапазоном присваивать тип с меньшим диапазоном, с учетом знака;
  • избегать в присваиваниях знаковому типу значение беззнакового типа и наоборот.
Арифметические преобразования

Арифметические преобразования (arithmetic conversion) происходят в выражениях с арифметическими операциями в которых операнды имеют разные числовые типы. Арифметические преобразования выполняются по следующим правилам:

  • В выражениях с младшими целочисленными типами (bool, char и short) итоговый результат преобразуется в int, если результат соответствует диапазону int, иначе преобразуется в unsigned int;
  • если один из операндов имеет тип float, double или long double, то и другой операнд преобразуется в тип float, double или long double, соответственно;
  • если оба операнда имеют одинаковый знак (то есть: либо знакового, либо беззнакового типа), то операнд с младшим типом преобразуется к старшему типу;
  • Для выражений в которых имеется знаковый, и беззнаковый тип:

  • если беззнаковый тип операнда старше, чем операнд со знаком, то последний преобразуется в тип беззнакового операнда;
  • иначе, если тип со знаком может представить весь диапазон беззнакового типа, беззнаковый операнд преобразуется к типу операнда со знаком;
  • иначе операнды преобразуются в беззнаковую версию типа со знаком
Явное преобразование

В C++ для явного преобразования типов используется приведение типов (cast) в старом стиле и с помощью операции static_cast.
Приведение типов в старом стиле использует две формы:

  • type(expression) – в стиле C++
  • (type)expression – в стиле C
где type — тип к которому приводится выражение expression.
И та, и другая форма является устаревшей и небезопасной. Она существует до сих пор по причине совместимости с языком С.
В С++ рекомендуется использовать новый формат преобразования типов: операцию static_cast. Эта операция позволяет предупреждать некорректную попытку преобразования типа. Формат операции таков:

static_cast<type>(expression)

Операции инкремента и декремента

Унарные операции икремента и декремента применяются в том случае, когда аргумент приращения целой переменной в операциях сложения и вычитания равен 1. Они имеют две формы: постфиксную и префиксную:

Таблица 2. Операции инкремента и декремента
Постфиксный (суффиксальный) Префексный
Инкремент
a++ ++a
Декремент
a-- --a

Операция инкремента увеличивает значение переменной на единицу, а декремента уменьшают на единицу. Постфиксная и префиксная формы различаются тем, что при выполнении инструкций значение переменной будет изменяться так: для постфиксной формы - после выполнения инструкции, для префиксной формы - до выполнения инструкции. Это иллюстрирует следующий пример:

Программа 8.5.3
#include <iostream>
using namespace std;

int main() {
	int a = 5;
	cout << a++ << "\n"
	     << a   << "\n"
	     << ++a << endl;
	return 0;
}

На выводе:

5
6
7

Сначала выводится выражение с постфиксным инкрементом. Будет выведено первоначальное значение переменной, а затем происходит приращение и в следующей строке выводится уже измененное значение. В третьей строке вывода - сначала происходит приращение, а затем вывод измененного значения.
Операции декремента --a и a-- эквивалентны присваиваниям:

a = a - 1
a -= 1 

Аналогично операции инкремента ++a и a++ эквивалентны присваиваниям:

a = a + 1
a += 1 

Однако, поскольку операции инкремента и декремента применяются довольно часто их реализация отличается от операций с присваиванием, что позволяет им работать быстрее аналогичных операций с присваиванием.

Операции инкремента и декремента нельзя применять к объекту типа bool.

Форма сокращенных операций

Разработчики языка C++ стремились сделать программный код более лаконичным и компактным. Еще одним способом сократить запись выражений является сокращенная форма присваивания. Используется она тогда, когда значение переменной, с помощью арифметических операций, изменяется значением другой переменной или более сложным выражением:

Полная форма    Краткая форма
a = a + b       a += b
a = a - b       a -= b
a = a * b       a *= b
a = a / b       a /= b
a = a % b       a %= b
a = a << b      a <<= b
a = a >> b      a >>= b
a = a & b       a &= b
a = a ^ b       a ^= b
a = a | b       a |= b

Пробелы между операцией "=" и арифметической операцией не допускаются.

Операции целочисленного деления

С целым типом связаны несколько типичных задач. Наиболее распространенные - это работа с разрядами числа и остатками от деления. Обе эти задачи используют операции деления целых чисел. Таких операций две:

  • Получение целой части от деления – "/"
  • Получение остатка от деления – "%"
Обе операции (а также операция умножения - "*") имеют равный приоритет, поэтому в программе 8.5.4 вычисление отдельных разрядов взято в скобки.
Операции целочисленного деления имеют дефект - если делимое число является отрицательным числом, то остаток от деления (вне зависимости от знака делителя) будет тоже отрицательным числом, что расходится с правилами математики.
Задача 2. Дано трехзначное число получить сумму и произведение разрядов этого числа.

Программа 8.5.4
//====================================================//
// Получение суммы и произведения                     //
// разрядов трехзначного числа                        //
//====================================================//
#include <iostream>
using namespace std;

int main() {
	int n;
	cout << "n = "; cin >> n;
	cout << "Сумма цифр числа = "
		 << n % 10 + n / 10 % 10 + n / 100
		 << "\nПроизведение цифр числа = "
		 << (n % 10) * (n / 10 % 10) * (n / 100)
		 << endl;
	return 0;
}

Обсуждение. Поскольку мы используем десятичную систему счисления, то остаток от деления числа на 10 даст нам младший разряд десятичного числа. Аналогичными действиями мы можем получать разряды числа в другой системе счисления (однако делать это лучше в циклах, поскольку не ясно сколько разрядов будет иметь такое число).
Типичным примером задачи на работу с остатками является задачи на определение долей времени.
Задача 3. Дано время в виде целого числа секунд (3600 < t < 1000000000000000000). Определить количество прошедших полных суток и количество часов минут и секунд прошедших с начала последних суток.

Программа 8.5.5
#include <iostream>
using namespace std;

int main() {
	cout << "Введите время в секундах\n"
			"(3600 < t < 1000000000000000000):\n";
	unsigned long t;
	cin >> t;
	unsigned long d = t / 3600 / 24;
	int h = t / 3600 % 24;
	int m = t % 3600 / 60;
	int s = t % 3600 % 60;
	cout << "Дней = " << d
		 << "\nЧасов = " << h
		 << "\nМинут = " << m
		 << "\nСекунд = " << s
		 << endl;
	return 0;
}

Вывод

Введите время в секундах
(3600 < t < 10000000000000000000):
123456
Дней = 1
Часов = 10
Минут = 17
Секунд = 36

Приложение

Примеры решения задач
Вопросы
Темы сообщений
Задания А
  1. Даны стороны прямоугольника a и b. Найти его площадь S = a·b и периметр P = 2·(a + b).
  2. Дана длина L окружности. Найти ее радиус R и площадь S круга, ограниченного этой окружностью, учитывая, что L = 2·π·R, S = π·R2.
  3. Дано трехзначное число. Вывести число, полученное при прочтении исходного числа справа налево.
  4. С начала суток прошло N секунд (N — целое). Найти количество секунд, прошедших с начала последнего часа.
Задания Б
Задания С
Ссылки
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (1 оценок, среднее: 5,00 из 5)
Загрузка...

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.


Обсуждение закрыто.