Выражения

Содержание

Выражения состоят из операторов, констант, функций и переменных. В языке С выражением является любая правильная последовательность этих элементов. Большинство выражений в языке С по форме очень похожи на алгебраические, часто их и пишут, руководствуясь правилами алгебры. Однако здесь необходимо быть внимательным и учитывать специфику выражений в языке С.

Порядок вычислений


Порядок вычисления подвыражений в выражениях языка С не определен. Компилятор может самостоятельно перестроить выражение с целью создания оптимального объектного кода. Это значит, что программист не может полагаться на определенную последовательность вычисления подвыражений. Например, при вычислении выражения

х = 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.2. Пример преобразования типов

Явное преоразование типов: операция приведения типов


Программист может «принудительно» преобразовать значение выражения к нужному ему типу, используя операцию приведения типов. Общая форма оператора явного приведения типа:

(тип) выражение

Здесь тип — это любой поддерживаемый тип данных. Например, следующая запись преобразует значение выражения х/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;