C++. Бархатный путь. Часть 1 - Преобразование основных типов

ОГЛАВЛЕНИЕ

 

Преобразование основных типов

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

Однако не всегда в программе удаётся легко согласовать типы операндов и параметров. И здесь проблем, связанных с согласованием типов операндов и параметров транслятор берёт на себя. Фактически это означает введение ещё одной системы правил, которая называется правилами стандартного преобразования типов.

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

1.Присвоение "большему типу" значения "меньшего типа". Безопасное присвоение, гарантирует сохранение значения.

unsigned int UnsignedIntVal; unsigned char UnsignedCharVal; UnsignedIntVal = UnsignedCharVal;

2.Присвоение "меньшему типу" значения "большего типа". Потенциально опасное присвоение, грозит потерей информации.

int IntVal; char CharVal; CharVal = IntVal;

3.Преобразование значения из "меньшего типа" в "больший тип". Называется расширением типа.

(unsigned int)UnsignedCharVal;

4.Преобразование значения из "большего типа" в "меньший тип". Называется сужением типа. Является опасным преобразованием.

(char)IntVal;

Корректное выполнение действий со значениями различных типов в безопасных случаях и в ряде опасных случаев обеспечивается благодаря реализованной в C++ системе преобразования типов. 

При трансляции выражений с различными типами операндов транслятор использует механизмы неявных преобразований, которые основываются на следующих правилах стандартных преобразований:
Присваивание значения объекту преобразует это значение к типу объекта.
unsigned int MyIntU;
MyIntU = 3.14159;
Эквивалентно
MyIntU = (unsigned int)3.14159;
Передача значения при вызове функции преобразует это значение в тип параметра функции. Он становится известен благодаря прототипу вызываемой функции.
void ff(int); // Прототип функции.
:::::
ff(3.14159);

Эквивалентно
ff((int)3.14159);

При этом на стадии трансляции возможно появление предупреждения о сужении типа.

В арифметическом выражении тип результата выражения определяется самым "широким" типом среди всех образующих выражение операндов. Этот тип называют результирующим типом выражения. К этому типу преобразуются все остальные операнды.
unsigned int MyIntU = 5;
…(MyIntU + 3.14159)…

Результирующим типом выражения здесь оказывается тип double, представленный в выражении литералом 3.14159. В процессе вычисления выражения значение переменной MyIntU преобразуется в 5.0, к которому прибавляется 3.14159.

Преобразование типа при вычислениях арифметических выражений применяется к копиям значений образующих выражение подвыражений. В процессе преобразования типов результаты преобразований подвыражениям не присваиваются.
unsigned int MyIntU = 5;
MyIntU = MyIntU + 3.14159;

Здесь имеют место два последовательных преобразования:

По ходу вычисления выражения значение переменной MyIntU расширяется до double и к расширенной копии значения 5.0 прибавляется 3.14159. После этого результирующее значение 8.14159, в соответствии с первым правилом, сужается до типа unsigned int. В результате чего получается значение 8, которое и присваивается переменной MyIntU.

Указатель на любой не являющийся константой тип можно присваивать указателю типа void*. Этот указатель способен адресовать объекты любого типа данных. Он используется всякий раз, когда неизвестен тип объекта.
int iVal;
int *p_iVal = 0;
char *p_chVal = 0;
void *p_Val;
const int *pc_iVal = &iVal;
p_Val = p_iVal;
p_Val = p_chVal;
// ПРАВИЛО 5 выполняется...
p_Val = pc_iVal;
//Ошибка: pc_iVal - указатель на константу.
const void *pcVal = pc_iVal;
/*
А здесь всё хорошо! Указателю на константу присвоен указатель на константу.
*/

Перед операцией разыменования указатель типа void* нужно явно преобразовать в указатель на конкретный тип, поскольку в этом случае отсутствует информация о типе, подсказывающая транслятору способ интерпретации битовой последовательности, представляемой указателем:

char *p_chValName = "Marina";
p_Val = p_chValName;
p_chVal = (char*)p_Val; /*Явное приведение.*/
Механизм неявных преобразований может быть отключён посредством явного указания в тексте программы требуемого преобразования типов.

Так, модификация ранее рассмотренного примера

MyIntU = MyIntU + (int)3.14159;

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