целочисленный тип. Результат выполнения рассматриваемого оператора, если один из операторов отрицателен, зависит от реализации.
Бинарные операторы “ – ” и “+” имеют одинаковый приоритет, который ниже приоритета операторов “*”, “/” и “%”. Приоритет унарных операторов выше приоритета бинарных операторов. Унарные операторы имеют правую ассоциативность, а бинарные – левую ассоциативность.
15.3.Преобразования типа при выполнении
бинарных операторов
Вычислять значение выражения можно только в том случае, если его операнды имеют одинаковые типы. Компилятор сильно типизированного языка должен “отказываться” обрабатывать выражения, операнды которых имеют разные типы. Однако такой подход не всегда является оправданным. Дело в том, что существуют близкие по своему назначению типы. К таким, например, типам относятся арифметические типы. В распоряжение программиста язык Си предоставляет большое количество разновидностей арифметических типов. Представляется целесообразным допускать использование некоторых категорий смешанных выражений, операнды которых имели бы разные, но близкие по своему назначению типы. Такой подход реализован в языке Си.
В языке Си предусмотрена возможность выполнения преобразования типов при вычислении смешанных арифметических выражений. Назначение преобразования типа состоит в том, чтобы привести операнды к одному общему типу. Язык предусматривает две разновидности преобразования типов;
●Неявное (автоматическое).
●Явное.
Остановимся вначале на автоматическом преобразовании типов.
Правила выполнения автоматических преобразований типов таковы, что обычно не приводят к потере информации. Дело в том, что при их использовании операнды с меньшим диапазоном значений преобразуются в операнды с большим диапазоном значений, как, например, преобразование целого числа в вещественное число в выражении 2 + 5.3.
При выполнении автоматических преобразований типов все арифметические типы разделены на две категории:
●Короткие типы
●Длинные типы.
Воснову этой классификации положен размер памяти, которую занимают данные.
К коротким типам относятся следующие типы:
▪char,
▪signed char,
31
▪unsigned char,
▪short,
▪unsigned short.
Для всех коротких типов при преобразованиях типов вначале выполняется операция, которая называется целочисленным повышением: все короткие типы преобразуются к типу int. Для этого правила имеется одно уточнение. Тип unsigned short преобразуется в тип int только в том случае, когда этот тип (int) достаточен для представления всего диапазона значений типа unsigned short (обычно это имеет место в тех системах, где для представления данных типа short отводится половина машинного слова и полное слово – для данных типа int). В последнем случае тип unsigned short преобразуется в unsigned int. Рассмотрим программный код, приведенный ниже:
#include<stdio.h>
#include<conio.h> int main(void)
{
short n = 5; char ch = ‘A’;
printf(“n + ch = %d sizeof(n + ch)=\n”, n + ch, sizeof(n + ch));
getch(); return 0;
}
В вызове функции printf() вычисляется значение выражения n + ch и размер памяти, который занимает результат вычисления этого значения. При выполнении этой программы в среде Builder v . 6 на экране дисплея получен следующий результат:
n + ch = 70 sizeof(n + ch)=4
Значение переменной ch, преобразованное к типу int, будет равно 65. Сложение этого числа со значением переменной n и дает в качестве результата вычисления выражения n + ch число 70. Завершающая часть вывода (sizeof(n + ch) = 4) может рассматриваться как доказательство того положения, что вычисления действительно выполнялись с операндами, преобразованными к типу int.
При преобразовании длинных типов используется принцип ранжирования. С каждым из таких типов связывается его ранг. Ранжированная последовательность длинных типов, начиная с типа, имеющего наивысший ранг, имеет следующий вид:
●long double,
●double,
●float,
●unsigned long long,
●long long,
●unsigned long,
●long,
●unsigned,
32
● int.
При использовании в выражении операндов, имеющих разные ранги, тип операнда, имеющий меньший ранг, приводится к типу операнда, имеющего больший ранг. Например, если в выражении один операнд имеет тип double, а второй – long, то тип второго операнда должен быть преобразован к типу double.
Из этого правила существует одно исключение. Если один из операндов имеет тип long, а второй - unsigned, причем не все значения unsigned int могут быть представлены типом long, то оба операнда преобразуются к типу unsigned long.
Явное преобразование типа основано на применении оператора приведения типов. Общий вид применения оператора приведения типа имеет
следующий вид:
(тип ) выражение
Здесь тип – это любой тип, который поддерживается языком Си. Например, следующая запись позволяет преобразовать значение выражения x * y / 5 к типу float:
(float) (x * y / 5)
В отличие от автоматических явные преобразования типов могут быть как безопасными, так и опасными.
Оператор приведения типа является унарным оператором и имеет тот же приоритет, что и другие унарные операторы.
При написании арифметических выражений часто приходиться использовать стандартные математические функции, прототипы которых находятся в заголовочном файле math.h. Вызов функции состоит из ее имени и заключенного в круглые скобки списка фактических параметров. В качестве фактического параметра математических функций можно использовать арифметическое выражение. Ниже в таблице приведены прототипы некоторых математических функций.
Имя |
Прототип |
Описание |
|
|
|
функции |
int abs(int num); |
|
|
|
|
abs |
Вычисление модуля аргумента num |
||||
ceil |
double ceil(double num); |
Возвращает |
наименьшее |
целое, |
|
|
|
которое |
удовлетворяет |
условию |
|
|
|
>= num. Обратите внимание на тип |
|||
|
double cos(double num); |
возвращаемого значения (double). |
|||
cos |
Вычисляет |
значение косинуса от |
|||
|
|
аргумента |
num. |
Значение |
аргумента |
|
double fabs(double num); |
должно быть задано в радианах. |
|||
fabs |
Вычисление |
значение |
модуля |
||
|
double floor(double num); |
аргумента num |
|
|
|
floor |
Возвращает |
наибольшее |
целое, |
||
33
|
|
которое удовлетворяет |
условию |
|
||
|
double exp(double num) |
<= num. |
|
|
|
|
exp |
Вычисляет |
значение |
экспоненты |
от |
||
|
double log(double num); |
аргумента num |
|
|
|
|
log |
Вычисляет |
значение |
натурального |
|||
|
double log10(double num); |
логарифма от аргумента num. |
|
|||
log10 |
Вычисляет |
значение |
логарифма |
по |
||
|
double pow(double base, |
основанию 10 от аргумента num. |
|
|||
pow |
Вычисляет |
значение аргумента base, |
||||
|
double x); |
возведенное в степень x. |
|
|
||
sin |
double sin(double num); |
Вычисляет |
значение |
синуса |
от |
|
|
|
аргумента |
num. Значение |
аргумента |
||
|
double sqrt(double num); |
должно быть задано в радианах. |
|
|||
sqrt |
Вычисляет |
значение |
корня |
|||
|
double tan(double num); |
квадратного от аргумента num. |
|
|||
tan |
Вычисляет |
значение |
тангенса |
от |
||
|
|
аргумента |
num. Значение |
аргумента |
||
|
|
должно быть задано в радианах. |
|
|||
Вязыке Си различают две формы оператора присваивания:
●простой оператор присваивания,
●составной оператор присваивания.
Составной оператор отличается наличием дополнительной операции, которая выполняется до присваивания. Рассмотрим вначале простой оператор присваивания.
Любой оператор присваивания, не входящий в состав другого выражения, может выступать в качестве инструкции присваивания, если после него поставить точку с запятой. Например, n = 5 – это оператор присваивания, а n = 5; - это инструкция присваивания.
Основное назначение оператора присваивания в языках программирования состоит в выполнении следующих операций:
●в запоминании в памяти входных данных и результатов вычислений некоторых выражений.
●в перемещении данных из одной области памяти в другую.
Вязыке Си присваивание имеет расширенное по сравнению с другими языками (Паскаль и Бейсик) назначение. Дело в том, что в языке Си может использоваться возвращаемое оператором присваивания значение. Это связано с тем обстоятельством, что присваивание в языке Си относится к категории операторов (operator), а не инструкций (statement), как это имеет место в других языках. Приведем пример, в котором используется значение, возвращаемое оператором присваивания.
#include<stdio.h>
#include<conio.h>
34
int main(void)
{
int x = 10; int y = 20;
printf("%d\n", x = y); /* ... */
getch(); return 0;
}
Вэтом программном коде вызов функции printf() используется для вывода на дисплей результата выполнения оператора присваивания.
Оператор присваивания относится к категории бинарных операторов. В качестве знака присваивания в языке Си используется математический знак равенства “=”. При выполнении оператора присваивания вначале вычисляется его правый операнд, затем вычисленное значение записывается
вобласть памяти, адрес которой определяется его левым операндом. Требование знания адреса накладывает ограничения на выражение, которое может использоваться в качестве левого операнда присваивания. К левому операнду должна быть допустима операция взятия адреса. Выражения, допускающие применение этой операции, принято называть lvalue выражениями. Различают две категории lvalue выражений:
●изменяемые,
●неизменяемые.
Вкачестве левого операнда присваивания следует применять модифицируемое lvalue выражение.
Возвращаемым значением оператора присваивания является значение, записанное в его левый операнд. В книгах по языку Си и в сообщениях компилятора за левым операндом оператора присваивания закрепился термин lvalue (left side value), а за правым - rvalue (right side value) Таким образом, в общем случае простой оператор присваивания имеет следующий формат:
модифицируемое_lvalue_выражение = rvalue_выражение
Простейшим видом модифицируемого_lvalue_выражения является простая переменная, в объявлении которой отсутствует зарезервированное слово const. К числу других допустимых выражений относятся выражения, используемые для доступа к элементам массивов и структур. В качестве правого операнда оператора присваивания может использоваться произвольное выражение, тип которого совместим по присваиванию с типом операнда, стоящего слева от знака присваивания. Все арифметические типы в языке Си совместимы по присваиванию.
Приведем примеры операторов присваивания. Пусть x и y – переменные, имеющие тип double. Тогда допустимы следующие операторы присваивания.
x = 4.5 y = x - 3
35