основыкнигиwin/dos*nixготовоелирикагостиФОРУМПОИСК
Б.В. Керниган, Д.М. Ричи -- Язык C

2.12. Старшинство и порядок вычисления

      В приводимой ниже таблице сведены правила старшинства и ассоциативности всех операций, включая и те, которые мы еще не обсуждали. Операции, расположенные в одной строке, имеют один и тот же уровень старшинства; строки расположены в порядке убывания старшинства. Так, например, операции *, / и % имеют одинаковый уровень старшинства, который выше, чем уровень операций + и -.

   OPERATOR                               ASSOCIATIVITY
 
   () [] -> .                             LEFT TO RIGHT
 
   !  \^ ++  --  -  (TYPE)  * &  SIZEOF   RIGHT TO LEFT
 
   *  /  %                                LEFT TO RIGHT
 
   +  -                                   LEFT TO RIGHT
 
   <<  >>                                 LEFT TO RIGHT
 
   <  <= >  >=                            LEFT TO RIGHT
           
   ==  !=                                 LEFT TO RIGHT
 
   &                                      LEFT TO RIGHT
 
   ^                                      LEFT TO RIGHT
 
   \!                                     LEFT TO RIGHT
 
   &&                                     LEFT TO RIGHT
 
   \!\!                                   LEFT TO RIGHT
 
   ?:                                     RIGHT TO LEFT
 
   =  += -=  ETC.                         RIGHT TO LEFT
 
   ,  (CHAPTER 3)                         LEFT TO RIGHT

Операции -> и . используются для доступа к элементам структур; они будут описаны в главе 6 вместе с SIZEOF (размер объекта). В главе 5 обсуждаются операции * (косвенная адресация) и & (адрес). Отметим, что уровень старшинства побитовых логических операций &, ^ и э ниже уровня операций == и !=. Это приводит к тому, что осуществляющие побитовую проверку выражения, подобные

 IF ((X & MASK) == 0) ...

для получения правильных результатов должны заключаться в круглые скобки.
      Как уже отмечалось ранее, выражения, в которые входит одна из ассоциативных и коммутативных операций (*, +, &, ^, э), могут перегруппировываться, даже если они заключены в круглые скобки. В большинстве случаев это не приводит к каким бы то ни было расхождениям; в ситуациях, где такие расхождения все же возможны, для обеспечения нужного порядка вычислений можно использовать явные промежуточные переменные.
      В языке "C", как и в большинстве языков, не фиксируется порядок вычисления операндов в операторе. Например в операторе вида

 X = F() + G();

сначала может быть вычислено F, а потом G, и наоборот; поэтому, если либо F, либо G изменяют внешнюю переменную, от которой зависит другой операнд, то значение X может зависеть от порядка вычислений. Для обеспечения нужной последовательности промежуточные результаты можно опять запоминать во временных переменных.
      Подобным же образом не фиксируется порядок вычисления аргументов функции, так что оператор

 PRINTF("%D %D\N",++N,POWER(2,N));

может давать (и действительно дает) на разных машинах разные результаты в зависимости от того, увеличивается ли N до или после обращения к функции POWER. Правильным решением, конечно, является запись

 ++N;
 PRINTF("%D %D\N",N,POWER(2,N));

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

 A[I] = I++;

Возникает вопрос, старое или новое значение I служит в качестве индекса. Компилятор может поступать разными способами и в зависимости от своей интерпретации выдавать разные результаты. Тот случай, когда происходят побочные эффекты (присваивание фактическим переменным), -- оставляется на усмотрение компилятора, так как наилучший порядок сильно зависит от архитектуры машины.
      Из этих рассуждений вытекает такая мораль: написание программ, зависящих от порядка вычислений, является плохим методом программирования на любом языке. Конечно, необходимо знать, чего следует избегать, но если вы не в курсе, как некоторые вещи реализованы на разных машинах, это неведение может предохранить вас от неприятностей. (Отладочная программа LINT укажет большинство мест, зависящих от порядка вычислений.

ПРЕДЫДУЩАЯ ЧАСТЬ
2.11. Условные выражения
СОДЕРЖАНИЕ СЛЕДУЮЩАЯ ЧАСТЬ
3. Поток управления