Явное и неявное преобразование типа
В параграфе операции кратко уже описывалась операция преобразования типа. В этом параграфе операция преобразования типа будет разобрана полностью с соответствующими примерами.
Операция явного преобразования типа в языке Си++ имеет две различные формы. Каноническую, унаследованную от языка Си, и функциональную, которые имеют следующий формат:
(type) операнд // каноническая форма
type (операнд) // функциональная форма
где type – основной, составной или определенный пользователем тип данных; операнд – константа, переменная или структурированный объект произвольного типа. Результатом выполнения операции явного преобразования типа является создание неименованного объекта типа type, который инициализируется значением операнда.
При явном преобразовании типа может происходить или не происходить потеря точности, хотя компилятор всегда старается по возможности преобразовать число с максимальной точностью. Всё зависит от того, из какого в какой тип происходит преобразование. Так, переменная типа long имеет 10 значащих цифр, а переменная типа float - только семь. Поэтому при преобразовании типа из long в float компилятор вынужден отбросить младшие разряды числа, сохранив при этом порядок и старшие разряды числа.
Double a = 1, d=2;
a+d;
int b=2;
a + b;
Примеры:
long k = 123456789; /* Определение переменной k (задаётся её тип – long и значение 123456789) */
float g = float(k); /* В соответствии с таблицей приоритетов операций сначала выполняется операция преобразования типа, т.е. float(k). Результатом выполнения этой операции является создание неименованной переменной типа float, которая инициализируется в данном случае с потерей точности значением переменной k (1234567e+2). При этом ни тип, ни значение переменной k не изменяются. Далее определяется переменная g типа float, которая инициализируется значением неименованной переменной. Область существования неименованной переменной – данная строка программы. При переходе к следующей строке программы неименованная переменная уничтожается.*/
cout<<”\n float(k) = “<<float(k); /*Оператор выведет на экран (float(k) = 1.234567e+08) результат операции преобразования типа, на примере которого можно видеть, что младшие разряды отброшены. */
double pi = 3.14159;
cout<<”\n int(pi) = “<<int(pi); /*Результатом выполнения этих строк программы будет вывод строки программы int(pi) = 3 на экран монитора. Дробная часть числа отброшена. */
cout<<”\n double(k) = “<<double(k); /* Результат (double(k) = 1.23456789е+08) потеря точности при преобразовании типа не происходит, так как число double имеет 15 значащих цифр. */
Фактически, приведённые формы операции преобразования типа не накладывают на программиста никаких ограничений на используемые в операции преобразования типа операнды, что иногда приводит к созданию небезопасного кода.
Поэтому в языке Си++ появились ещё несколько вариантов операции преобразования типа вида: static_cast<type>(value), dynamic_cast<type*>(ptr_value) или dynamic_cast<type&>(ref_value), const_cast<type>(const_value), reinterpret_cast<type>( value ), где type – это тип к которому будет преобразован операнд (value).
Как видно из приведенных выражений dynamic_cast может работать только с указателями ptr_value или ссылками ref_value; const_cast предназначен для преобразования именованных констант const_value. Конкрентные примеры использования различных операций преобразования типа будут приведены далее по мере изучения материала.
Перечисленные выше примеры, в которых использовалась функциональная форма операции преобразования типа, можно переписать с помощью static_cast следующим образом:
long k = 123456789;
float g = static_cast<float>(k);
double pi = 3.14159;
cout<<”\n int(pi) = “<<static_cast<int>(pi);
Кроме явного преобразования типа для арифметических операций и арифметических типов данных в языке Си++ определена операция неявного преобразования типа. Суть её состоит в том, что если в арифметической операции участвуют два операнда разного типа, то происходит неявное преобразование типа одного операнда к типу другого и только после этого происходит выполнение операции. Из двух возможных типов операндов компилятор всегда выбирает наиболее точный (в соответствии со схемой, изображенной на рис. 3.2) и преобразует к нему один из операторов. Этот же тип является типом результата операции.
Рис. 3.2. Схема арифметических преобразований типов, гарантирующих сохранение значимости
Из рис. 3.2 видно, что если в арифметической операции участвуют два операнда, например, типа int и double, то операнд типа int будет неявно преобразован к типу double, так как double точнее int, затем выполнится арифметическая операция, результат которой будет иметь тип double. Пример:
int f = 2;
double e = 2.71;
f = e*f; /*f неявно преобразуется к типу double; выполняется операция умножения ; результат операции равен 5.42 и имеет тип double. */
f= 1-e; /* константа единица имеет тип int, который по умолчанию преобразуется к типу double, выполняется операция вычитания; результат операции равен -1.71 типа double.*/
При записи арифметических выражений необходимо помнить, что правило неявного преобразования типа и все рассуждения, связанные с сохранением точности, относятся не ко всему выражению в целом, а к отдельно взятым операциям. Так, например:
int a =1, b = 2;
double f = 1;
a = a/b*f+a*f/b;
cout<<”\n f = “<<f; /* На экран монитора выводится результат f = 0.5, хотя логичнее было бы предположить, что результат должен быть равен единице. */
Полученный результат объясняется тем, что операции в выражении выполняются последовательно друг за другом в строгом соответствии с их рангом и направлением ассоциативности. Следовательно, первой выполнится операция (a/b). Оба операнда у неё имеют тип int, поэтому неявного преобразования типа не происходит; результатом операции является неименованная переменная типа int, значение которой равно нулю (дробная часть при делении целых чисел отбрасывается). Второй выполняется операция умножения неименованной переменной типа int на переменную f типа double, в результате умножения нуля типа int на единицу типа double получаем ноль типа double. Далее выполняется операция (а*f), в результате получаем единицу типа double. Единица типа double делится на переменную b типа int, получаем 0.5 типа double. Ноль типа double складывается с 0 типа double, получаем 0.5 типа double. Результат выполнения выражения a/b*f+a*f/b равен 0.5 типа double.
Таким образом, потеря точности произошла во время выполнения одной из операций (в данном случае a/b) и значение всего выражения стало не тем, которое можно было бы ожидать. Приведём ещё несколько примеров
Short int a = 10000, b = 5;
double c = 1.;
c = a*b/c; /* потеря точности происходит при выполнении операции a*b. Так как оба операнда имеют тип int, то результат тоже будет иметь тип int. Но в результате выполнения операции a*b получается значение 50000, которое выходит за допустимый диапазон значений переменных типа int. Поэтому значение выражения a*b/c будет непредсказуемо. */
unsigned int a = 5, b=2;
double k = 1;
k = k*(a-b); ./*Результат выполнения операции (a-b) имеет тип unsigned int, а число –65 выходит за допустимый диапазон значений типа unsigned int, поэтому значение выражения k*(a-b) будет непредсказуемо. */
k = 1/2*b;/*Значение выражения 1/2*b равно нулю. Так как первой выполняется операция деления, оба операнда - целые константы. Следовательно, результат выполнения операции тоже будет иметь тип int и будет равен нулю (при делении целых операндов дробная часть отбрасывается). Чтобы получить правильный результат, нужно сделать хотя бы одну константу константой вещественного типа. Для этого рядом с ней нужно поставить точку 1./2*b. */
Таким образом, при вычислении арифметических выражений может быть потеряна точность при выполнении какой-то операции или из-за отбрасывания дробной часть, или из-за переполнения разрядной сетки машины. Чтобы этого не происходило, лучше в выражении использовать только вещественные типы данных.
Неявное преобразование типа над арифметическими типами данных происходит также при выполнении операции присваивания (=). Операция заключается в том, что если арифметический тип данных, стоящий справа от операции (=), не совпадает с типом левого арифметического операнда, то происходит неявное преобразование типа правого операнда к типу левого в независимости от того, какой из типов точнее и теряется при выполнении этой операции точность или нет. Пример:
int k;
double pi= 3.1415;
k = pi; /*Создаётся неименованная переменная типа int, которая инициализируется значением переменной pi; при этом происходит потеря точности. Ни тип, ни значение переменной pi не изменяются. После этого значение неименованной переменной присваивается переменной k.*/
Таким образом, правило преобразования типа данных по умолчанию над арифметическими типами данных для арифметических операций и операции присваивания отличаются друг от друга.
Проектные задания
1. Набрать и отладить прогграмму 1.1. Модифицировать программу так, чтобы по центру экрана выводилось Фамилия И.О. учащегося.
2. Переведите в двоичный и шестнадцатеричный вид год Вашего дня рождения. Для проверки полученного результата осуществите обратный перевод с двоичной и шестнадцатеричной системы в десятичную.
3. Запишите свою фамилию, используя управляющие символы ESC-последовательности и ASCII коды таблицы 1251 – MS Windows.
4. В выражении (3+4>5 && 3+5 > 4 && 4+5>3) содержится 8 операций. Пометьте цифрами, в какой последовательности будут выполняться эти 8 операций. Найдите вручную, чему равно значение выражения (3+4>(5 && 3)+5 > 4 && 4+5>3). Объясните почему
5. Приведите примеры явного и неявного преобразования типов данных при выполнении арифметических операций и операции присваивания.
6. Напишите функцию, вычисляющую модуль, и функцию, вычисляющую аргумент комплексного числа.
7. Набрать и отладить программу 2.7, выполнить её в пошаговом режиме. Выписать значения фактических параметров при вызове функций
8. Набрать и отладить программу 2.8. Модифицировать её так, чтобы найти значения sin(π/4), cos(π/4) и tg(π/4)
9. Набрать и отладить программу 3.1, выполнить её в пошаговом режиме. Рассказать какой тип, класс памяти, продолжительность существования, сфера действия имени, видимость и тип компоновки имеют все переменные в данной программе.
10. Набрать и отладить программу 3.2. Убедится в том, какие значения принимают глобальные переменные с внутренней и внешним типом компоновки.
Дата добавления: 2020-12-11; просмотров: 477;