Язык программирования Си. Издание 3-е, исправленное
Шрифт:
Если значение преобразуется из целого в величину с плавающей точкой и она находится в допустимом диапазоне, но представляется в новом типе неточно, то результатом будет одно из двух значений нового типа, ближайших к исходному. Если результат выходит за границы диапазона допустимых значений, поведение программы не определено.
A6.4. Типы с плавающей точкой
При преобразовании из типа с плавающей точкой меньшей точности в тип с плавающей точкой большей точности значение не изменяется. Если, наоборот, переход осуществляется от большей точности к меньшей и значение остается в допустимых пределах нового типа, то результатом будет
А6.5. Арифметические преобразования
Во многих операциях преобразование типов операндов и определение типа результата осуществляются по одним и тем же правилам. Они состоят в том, что операнды приводятся к некоторому общему типу, который также является и типом результата. Эти правила называются обычными арифметическими преобразованиями.
• Если какой-либо из операндов имеет тип long double, то другой приводится к long double.
• В противном случае, если какой-либо из операндов имеет тип double, то другой приводится к double.
• В противном случае, если какой-либо из операндов имеет тип float, то другой приводится к float.
• В противном случае для обоих операндов осуществляется целочисленное повышение: затем, если один из операндов имеет тип unsigned long int, то и другой преобразуется в unsigned long int.
• В противном случае, если один из операндов принадлежит типу long int, а другой - unsigned int, то результат зависит от того, покрывает ли long int все значения unsigned int, и если это так, то unsigned int приводится к long int; если нет, то оба операнда преобразуются в unsigned long int.
• В противном случае, если один из операндов имеет тип long int, то другой приводится к long int.
• В противном случае, если один из операндов - unsigned int, то другой приводится к unsigned int.
• В противном случае оба операнда имеют тип int.
Здесь есть два изменения. Во-первых, арифметика с операндами с плавающей точкой теперь может производиться с одинарной точностью, а не только с двойной; в первой редакции языка вся арифметика с плавающей точкой производилась с двойной точностью. Во-вторых, более короткий беззнаковый тип в комбинации с более длинным знаковым типом не распространяет свойство беззнаковости на тип результата; в первой редакции беззнаковый тип всегда доминировал. Новые правила немного сложнее, но до некоторой степени уменьшают вероятность появления неожиданных эффектов в комбинациях знаковых и беззнаковых величин. При сравнении беззнакового выражения со знаковым того же размера все же может возникнуть неожиданный результат.
A6.6. Указатели и целые
К указателю можно прибавлять (и вычитать из него) выражение целочисленного типа; последнее в этом случае подвергается преобразованию, описанному в A7.7 при рассмотрении оператора сложения.
К двум указателям на объекты одного типа, принадлежащие одному массиву, может применяться операция вычитания; результат приводится к целому посредством преобразования, описанного в A7.7 при рассмотрении оператора вычитания.
Целочисленное константное выражение со значением 0 или оно же, но приведенное к типу void *, может быть преобразовано в указатель любого типа операторами приведения, присваивания и сравнения. Результатом будет NULL-указатель, который равен любому другому NULL-указателю того же типа, но не равен никакому указателю на реальный объект или функцию.
Для указателей допускаются и другие преобразования, но в связи с ними возникает проблема
Указатель можно привести к целочисленному типу, достаточно большому для его хранения; требуемый размер зависит от реализации. Функция преобразования также зависит от реализации.
Объект целочисленного типа можно явно преобразовать в указатель. Если целое получено из указателя и имеет достаточно большой размер, это преобразование даст тот же указатель; в противном случае результат зависит от реализации.
Указатель на один тип можно преобразовать в указатель на другой тип. Если исходный указатель ссылается на объект, должным образом не выровненный по границам слов памяти, то в результате может произойти ошибка адресации. Если требования на выравнивание у нового типа меньше или совпадают с требованиями на выравнивание первоначального типа, то гарантируется, что преобразование указателя в другой тип и обратно его не изменит; понятие "выравнивание" зависит от реализации, однако в любой реализации объекты типа char предъявляют минимальные требования на выравнивание. Как описано в A6.8, указатель может также преобразовываться в void * и обратно, значение указателя при этом не изменяется.
Указатель может быть преобразован в другой указатель того же типа с добавлением или удалением квалификаторов (A4.4, A8.2) того типа объекта, на который этот указатель показывает. Новый указатель, полученный добавлением квалификатора, имеет то же значение, но с дополнительными ограничениями, внесенными новыми квалификаторами. Операция по удалению квалификатора у объекта приводит к тому, что восстанавливается действие его начальных квалификаторов, заданных в объявлении этого объекта.
Наконец, указатель на функцию может быть преобразован в указатель на функцию другого типа. Вызов функции по преобразованному указателю зависит от реализации; однако, если указатель еще раз преобразовать к его исходному типу, результат будет идентичен вызову по первоначальному указателю.
A6.7. Тип void
Значение (несуществующее) объекта типа void никак нельзя использовать, его также нельзя явно или неявно привести к типу, отличному от void. Поскольку выражение типа void обозначает отсутствие значения, его можно применять только там, где не требуется значения. Например, в качестве выражения- инструкции (A9.2) или левого операнда у оператора "запятая" (A7.18). Выражение можно привести к тину void операцией приведения типа. Например, применительно к вызову функции, используемому в роли выражения-инструкции, операция приведения к void явным образом подчеркивает тот факт, что результат функции отбрасывается.
Тип void не фигурировал в первом издании этой книги, однако за прошедшее время стал общеупотребительным.
А6.8. Указатели на void
Любой указатель на объект можно привести к типу void * без потери информации. Если результат подвергнуть обратному преобразованию, то мы получим прежний указатель. В отличие от преобразований указатель-в-указатель (рассмотренных в A6.6), которые требуют явных операторов приведения к типу, в присваиваниях и сравнениях указатель любого типа может выступать в паре с указателем типа void * без каких-либо предварительных преобразований типа.