Выражения состоят из операторов, констант, функций и переменных. В языке С выражением является любая правильная последовательность этих элементов. Большинство выражений в языке С по форме очень похожи на алгебраические, часто их и пишут, руководствуясь правилами алгебры. Однако здесь необходимо быть внимательным и учитывать специфику выражений в языке С.
Порядок вычисления подвыражений в выражениях языка С не определен. Компилятор может самостоятельно перестроить выражение с целью создания оптимального объектного кода. Это значит, что программист не может полагаться на определенную последовательность вычисления подвыражений. Например, при вычислении выражения
х = f1() + f2();
нет никаких гарантий того, что функция f1() будет вызвана перед вызовом f2().
Если в выражении встречаются переменные и константы разных типов, они преобразуются к одному типу. Компилятор преобразует "меньший" тип в "больший". Этот процесс называется продвижением типов (type promotion). Сначала все переменные типов char и short int автоматически продвигаются в int. Это называется целочисленным расширением. (В С99 целочисленное расширение может также завершиться преобразованием в unsigned int.) После этого все остальные операции выполняются одна за другой, как описано в приведенном ниже алгоритме преобразования типов:
IF операнд имеет тип long double THEN второй операнд преобразуется в long double ELSE IF операнд имеет тип double THEN второй операнд преобразуется в double ELSE IF операнд имеет тип float THEN второй операнд преобразуется в float ELSE IF операнд имеет тип unsigned long THEN второй операнд преобразуется в unsigned long ELSE IF операнд имеет тип long THEN второй операнд преобразуется в long ELSE IF операнд имеет тип unsigned int THEN второй операнд преобразуется в unsigned int
Для тех, кто еще не знаком с общей формой оператора IF, приводим более русифицированную запись алгоритма:
ЕСЛИ операнд имеет тип long double ТО второй операнд преобразуется в long double ИНАЧЕ ЕСЛИ операнд имеет тип double ТО второй операнд преобразуется в double ИНАЧЕ ЕСЛИ операнд имеет тип float ТО второй операнд преобразуется в float ИНАЧЕ ЕСЛИ операнд имеет тип unsigned long ТО второй операнд преобразуется в unsigned long ИНАЧЕ ЕСЛИ операнд имеет тип long ТО второй операнд преобразуется в long ИНАЧЕ ЕСЛИ операнд имеет тип unsigned int ТО второй операнд преобразуется в unsigned int
Кроме того, действует следующее правило: если один из операндов имеет тип long, а второй — unsigned int, притом значение unsigned int не может быть представлено типом long, то оба операнда преобразуются в unsigned long.
На заметку | Описание правил целочисленного расширения в С99 см. в части II. |
После выполнения приведенных выше преобразований оба операнда относятся к одному и тому же типу, к этому типу относится и результат операции.
Рассмотрим пример преобразования типов, приведенный на рис. 2.2. Сначала символ ch преобразуется в целое число. Результат операции ch/i преобразуется в double, потому что результат f*d имеет тип double. Результат операции f+i имеет тип float, потому что f имеет тип float. Окончательный результат имеет тип double.
char ch; int i; float f; double d; r e s u l t = ( ch / i ) + ( f * d ) - ( f + i ) ; | | | | | | int | double | | float |_____| |_____| |_____| | | | int double float |_________________| | |__________________________| | double |
Программист может "принудительно" преобразовать значение выражения к нужному ему типу, используя операцию приведения типов. Общая форма оператора явного приведения типа:
(тип) выражение
Здесь тип — это любой поддерживаемый тип данных. Например, следующая запись преобразует значение выражения х/2 к типу float:
(float) х/2
Явное преобразование типа — это операция. Оператор приведения типа является унарным и имеет тот же приоритет, что и остальные унарные операторы.
Иногда приведение типов может быть весьма полезным. Допустим, целую переменную нужно использовать как параметр цикла, притом в вычислении участвует и дробная часть числа. В следующем примере показано, как с помощью приведения можно сохранить точность:
#include <stdio.h> int main(void) /* печать i и i/2 с дробной частью */ { int i; for(i=1; i<=100; ++i) printf("%d / 2 is: %f\n", i, (float) i /2); return 0; }
Без операции приведения (float) выполнялось бы целочисленное деление. Дробная часть результата выводится благодаря приведению типа переменной i.
Для повышения удобочитаемости программы при записи выражений можно использовать пробелы и символы табуляции. Например, следующие два оператора эквивалентны:
x=10/y~(127/x); x = 10 / y ~(127/x);
Лишние скобки, если они не изменяют приоритет операций, не приводят к ошибке и не замедляют вычисление выражения. Дополнительные скобки часто используют для прояснения порядка вычислений. В следующем примере 2-я строка читается значительно легче:
x = y/3-34*temp+127; x = (y/3) - (34*temp) + 127;