Т. Пратт, М. Зелковиц - Языки программирования - разработка и реализация (4-е издание_ 2002) (1160801), страница 56
Текст из файла (страница 56)
200 Глава 5. Элементарнь>е типы данных стве языков цель>е числа могут быть однозначно выражены через вещественные обьекты данных, короткие пелые числа могут быть расширены до вещественных без какой-либо потери информации. Но преобразование вещественного числа в целое, как правило, связано с потерей информапии. Хотя 1,0 в точности равно 1, число 1,5 уже невозможно преобразовать в целое число. Оно будет преобразовано в 1 или 2.
В данном случае приведение типа называется сужением н приводит к потере информации. При динамическом контроле тинов приведение осуществляется в момент обнаружения несоответствия типов при выполнении программы. В таких языках сужения могут быть допустимы для некоторых значений объектов данных (например, 1,0 можно преобразовать к целому типу, но 1,5 нельзя). При статическом контроле типов для реал нзапин таких сужений в программу при компиляции в нужных точках вставляется дополнительный код, который во время выполнения прог!>аммы и осугцсстш>яет сужение.
Но так как эффективность работы программы обычно является одним из основных требований, программисты стараются избегать применения сужений, чтобы не приходилось выполнять дополнительный код, определяющий нх допустимость. Операции преобразования тигюв могут потребовать значительных затрат компьютерного времени при выполнении программы. Например, в языках СОВОГО и РЕ>>!числа часто хранятся в виде символьных строк. В болыцннстве мац>ин операция сложения реализована таким образом, что зти символьные строки приходится преобразовывать в двоичный код, поддерживаемый аппаратной частью компьютера, а результат сложения снова преобразовывать в символьные строки для хранения в памяти.
Затраты времени на преобразование типов могут превосходить затраты на само сложение в сотни раз. Разработчики трансляторов языков тем нс менее иногда смешивают семантику объектов данных и способы их представления в памяти. Примером могут служить десятичные числа в СО В 01. и РЕ>>!. Трансляторы Р!.,>1 обычно хранят данные типа Е!ХЕ!3 ПЕС!МАЕ в упакованном десятичном формате. Это представление обеспечивается не>шсредствепно аппаратной частъю компьютера, но все же выполняется достаточно медлешнь В компиляторе Р1.,'С [32) данные типа Е!ХЕ() !)ЕС!>МАЕ хранятся в виде 1б-значных вещсственных чисел двойной точности с плавающей точкой.
При таком способе хранения нс происходит потери точности (что особенно важно при хранении десятичных чисел). Например, вычисление суммы 123А5 + 5г>3.21 приводит к довольно медленному сложению упакованных десятичных чисел (или еще более медленному програл>мне-моделируемому сложению, если упакованные десятичные данные не поддерживаются непосредственно аппаратной частью компьютера). В компиляторе Р(»>Сото же сложение осуществляется как единое действие, требукнцее гораздо меньших временных затрат: складываются даа числа с плавающей точкой 112345 + 54321, а компилятор следит за положением десятичной точки (в данном случае результат сложения должен быть умпожсн на 1О '), являющейся атрибутом резулътата, опредсляемым во время компиляпии. Сунгествус г два противоположных мнения относительно»пн>роты» применения неявного преобразования типов. В языках Разов! и А>)а оно почти не используется; любое несоответствие типо>>, за небольшим исключением, воспрц}имается 5.1.
Свойства типов и обьектов 201 как ошибка. В С приведение типов является правилом — когда компилятор обнаруживает несоответствие типов, он пытается отыскать подходящую операцик> преобразования, которую можно было бы вставить в компилируемый код для того, чтобы изменить нужным образом тип объекта данных.
Сообщение об ошибке возникает только в том случае, когда компилятор не может произвести требуемое преобразование типов. Несоответствие типов — это хотя и незначительная, по распространенная ошибка, и поэтому необходимость в преобразовании типов возникает достаточно часто, особенно в тех языках, в которых определено большое количество типов данных. Но значение понятия внесоотвстствие типов> нс так очевидно и с ним связан ряд сложных вопросов (см. раздел 6А). Приведение типов часто освобождает программиста от необходимости кропотливой работы, которая потребовалась бы для явного введения в программу операций преобразования типов. С другой стороны, приведение типов может скрыть наличие других, более серьезных ошибок, которые в противном случае могли бы быть замечены программистом при компиляции.
Язык Р(./1, в частности, печально известен как язык, компиляторы которого имеют тенденцию так «исправлять» незначительные ошибки (например, неправильно написанное имя переменной) посредством сложных преобразований типа, что их становится оче>п трудно обнаружить. Поскольку в Р).,у1допускатотся сужающие преобразования типов, иногда получаются удивительные результаты. Например, 9 + 10! 3 является недопустимым выражением! Чтобы понять это, заметим, что 10!3 преобразуется к виду 3. ЗЗЗЗЗЗЗ, (цифра 3 повторяется столько раз, сколько допускается реализацией языка на данном компьютере). Но в рсзультатс операции сложения 9 - 3 ЗЗЗЗЗ.. получается ошибка переполнения, так как в целой части результата добавляется сшс одна цифра. Если;>ту он>ибку прош норировать, то результат автоматически преобразуется к виду 2 33333..., а это совсем не то, что ожидалось.
5.1.5. Присваивание и инициализация Большая часть операций для обычных элементарных типов данных — в частности, для чисел, перечислений, булевых величин и символов — выполняется следующим образом: берутся один или два аргул>снта одного типа, производятся какие-то относительно простые арифметические операции, операции вычисления отношений или другие и вычисляется рсзультирующш1 объект данных, который может быть того же типа, что и аргументы, а может и отличаться от цпх. Операция присваивания выполняется более сложным образом и заслуживает отдельного рассмотрения.
Прнсваиванпе — это элементарная операция, которая изменяет связывание объекта данных со значением. Однако это изменение — побочиьш эс)>9>ект данной операции. В некоторых языках, например в С и 1!БР, операция присваивания также возвращает некоторый результат — объект данных, содержащий копию присвосннот о значения.
Зти факторы становятся более попятными, когда мы пытаемся написать спецификацию для операции присваивания. В Рапса! спецификация для операции присваивания выглядит так; присваиваииет;Ю целое,и целое, -> пустой тип Ьо>Ш 202 Глава 5. Элементарные типы данных Действие операции таково; установить значение, содержащееся в объекте данных целоец таким образом, чтобы оно стало копией значения, содержащегося в объекте данных целое,, но не возвращать явный результат. (Изменение значения объекта целое; является неявным результатом или побочным эффектом этой операции.) В С спецификация операции присваивания выглядит иначе: црцсвацвацие(-Х цецоевх целое; -« цегае, Действие операции таково: установить значение, содержащееся в обьекте данных а пГеВег „таким образом, чтобы оно стало копией значения, содержащегося в объекте данных а п»еперь а также создать и возвРатить новый объект данных а пГеВегв, содсР- жащий копию значения объекта 1п1ецег» Рассмотрим такое присваивание: Х;= Х.
Здесь интересно наблюдать за различными интерпретациями ссылки на переменную Х. Стояцхее справа Х обозначает величину, содержащуюся в переменной с этим именем. Подобные ссьикн называются правосторонници значениями (оператора присваивания), или г-значениями, объекта данных. Аналогично стоящее слева Х обозначает местоположение обьекта, в ко~ором будет содержаться новое значение. Такис ссылки называются левостороннпмн значениями (оператора присваивания), или Рзначениями. Тогда мы можем определить оператор присваивания следующим образом: 1) сначала вычисляется l-значение первого операнда выражения; 2) затем вычисляется г-значение второго операнда выражения; 3) присваивается вычисленное г-значение вычисленному Азначению объекта данных; 4) возвращается вычисленное г-значенис как результат выполнения операции.
Если у нас имеется операция присваивания (такая как, например, в С), то можно сказать, что она возвращает г-значение обьекта данных, которому присвоили новос значение. Помимо того, в С имеется рад унарных операций для манипуляций с г-значениями и Рзначсниями выражений, которые позволяют вьшолняпв множество полезных и порой странных действий, связанных с присваиванием. Такая двойственная «природа» операции присваивания — как функции изменения значения объекта данных (чсрез его 1-значение) и как функции, возвращающей значение (чсрсз г-значение обьекта данных), — широко используется в С (более подробно см. главу 8). Использование г-значений и l-значений позволяет более лаконично описывать семантику выражений.