
- •Данных. Составные структуры данных
- •3.2.1 Общие положения
- •3.2.1.1 Алфавит языка
- •3.2.1.2 Формат программы
- •3.2.1.3. Комментарии
- •// Пример однострочного комментария
- •3.2.1.4 Зарезервированные слова
- •3.2.2 Данные
- •3.2.2.1 Идентификаторы данных
- •3.2.2.2 Типы данных
- •3.2.2.3 Постоянные
- •3.2.2.4 Переменные
- •3.2.2.5 Массивы
- •3.2.2.6 Структуры
- •3.2.2.7 Объединения
- •Масив X:
- •Адреса элементу:
- •Масив y:
- •3.2.2.8 Перечисления
- •3.2.2.9 Указатели
- •Int Hour;
- •Int Press;
- •3.2.2.9.1 Динамическое выделения памяти для хранения данных
- •3.2.2.9.2 Динамическое освобождение памяти от хранимых данных
- •3.2.2.9.3 Ссылка
- •3.2.2.10 Множества
- •3.2.3 Выражения
- •3.2.3.1 Арифметические операции
- •Char int float | signed unsigned | short (базовый) long
- •3.2.3.2 Битовые операции
- •3.2.3.2 Логические операции
- •3.2.3.4 Операции отношения
- •3.2.3.5 Операция присваивания
- •3.2.3.6 Специальные операции
- •3.2.3.7 Элементарные функции
- •3.8 Приведение типов данных
- •3.2.3.9 Приоритеты операций
- •3.2.3.10 Адресные выражения
3.8 Приведение типов данных
Приведением типа данного называется операция принудительного изменения типа этого данного.
Тип данного или выражения можно определить динамически (т.е. во время выполнения программы) с помощью инструкции:
динамическое определение типа данного ::=
typeid, «(», ( выражение | тип данного ), «).name ( )»;
Для выполнения такой инструкции в программу нужно включить заголовочный файл typeinfo.h инструкцией #include <typeinfo.h>. Результатом будет строка – наименование типа данного, например, результатом вызова: typeid (4.5).name ( ) будет строка «double».
Язык программирования С++ предоставляет возможности для неявного и явного приведения типов данных.
Если в одном выражении присутствуют операнды разных типов, то выполняется неявное приведение типов операндов в соответствии правилами приоритетности по следующим признакам: тип, длина и знак (таблица 3.2.5).
Таблица 3.2.5
Признак |
Направления убывания приоритета |
||||
тип |
float |
int |
char |
bool |
void |
длина |
long |
(ordinal) |
short |
|
|
знак |
unsigned |
signed |
|
|
|
Некоторые преобразования (как явные, так и неявные) могут приводить к потере значности данного, если значение приводимого данного не укладывается в допустимый диапазон значений приведенного данного.
Рассмотрим пример неявного приведения типов целочисленных данных:
int Source = 300;
char Result = Source; // результат неявного приведения типа – значение 44
Неявное приведение типа выполняется так: вычисляется остаток от деления приводимого значения 300 на максимально допустимое значение целевого типа плюс один (255 + 1), и прибавляется к нижней границе значений для этого типа (0 + 44), а результат сложения (44) становится результатом приведения.
Нередко такие ошибки возникают, когда переменная типа с ограниченным диапазоном используется в качестве параметра цикла (например, переменная типа char многократно увеличивается, и её значение выходит из диапазона допустимых значений).
При приведении типов вещественных данных с большим диапазоном к типам с меньшим диапазоном (например, из float к double) также возможны конфликты. Например:
float Target = 1е+50;
Максимально допустимое значение данного типа float составляет +3.4·1038, поэтому значение 1е+50 (т.е. 1050) не может быть корректно представлено данным типа float. При этом в переменную Target запишется условная величина (+INF или –INF), обозначающая бесконечность. Однако пользователь об этом факте не будет извещен.
Вещественные значения преобразуются к целым значениям отбрасыванием дробной части числа, что также приводит к потере значности данного.
Описанные выше ошибки приведения типов данных далеко не всегда выявляются компилятором и исполняющей системой, что нередко приводит к трудновыявляемым ошибкам вычислений. Ответственность за отслеживание и корректность таких преобразований целиком возлагается на программиста.
Явное приведение типов данных приходится делать в тех случаях, когда компилятор не выполняет преобразования автоматически из-за потенциальной опасности операции. Примером небезопасного преобразования типа может служить приведение действительного типа к целому, что приводит к потере точности данного.
В языке С++ предусмотрены два стиля приведения типа данного:
традиционный стиль приведения типа данного::= «(«, тип данного, «)», выражение;
функциональный стиль приведения типа данного ::= тип данного, «(», выражение, «)»;
Примеры:
(double) 4 // результат: 4.0
int (4.5 + 5) // результат: 9
Приведение типов данных можно выполнить и с помощью инструкции static_cast:
статическое приведение типа ::= static_cast, «<», тип данного, «>», «(», идентификатор, «)»;
Эта инструкция используется в контексте типа данного и применяется во время компиляции для стандартных типов данных и типов данных, определенных пользователем. Например:
double X = 4.5;
int Y = static_cast <int> (X); // результат приведения типа – данное X типа int со значением 4
Инструкцию static_cast можно использовать для выполнения приведения типов, обратного неявному преобразованию. Так, C++ автоматически приводит элементы перечислений к типу int, но, если нужно выполнить обратное преобразование, то следует использовать static_cast. Также static_cast можно использовать для приведения указателей к типу void * и наоборот.
Кроме того, язык С++ допускает преобразование квалификаторов типов данных, - постоянных в переменные и наоборот (без изменения базового типа) с помощью инструкции const_cast:
статическое приведение постоянных/переменных ::=
const_cast, «<», тип данного, «>», «(», идентификатор, «)»;
Пример:
int X = 1, Y = 2;
int *const Z = &X; // Z - постоянный указатель на X (т.е. *Z = 1)
const_cast <int *> (Z) = &Y; // теперь переменный указатель Z указывает на Y (т.е. *Z = 2)