Книга: Язык программирования Си для персонального компьютера

Приоритет и порядок выполнения

Приоритет и порядок выполнения

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

В таблице 4.1 приведены операции в порядке убывания приоритета. Операции, расположенные в одной строке таблицы, или объединенные в одну группу, имеют одинаковый приоритет и одинаковую ассоциативность.

Таблица 4.1.

Приоритет и ассоциативность операций в языке Си

Знак операции Наименование Ассоциативность
() [] . -> Первичные Слева направо
+ - ~ ! * & ++ -- sizeof приведение типа Унарные Справа налево
* / % Мультипликативные Слева направо
+ - Аддитивные Слева направо
>> << Сдвиг Слева направо
< > <= >= Отношение Слева направо
== != Отношение Слева направо
& Поразрядное И Слева направо
^ Поразрядное исключающее ИЛИ Слева направо
| Поразрядное включающее ИЛИ Слева направо
&& Логическое И Слева направо
|| Логическое ИЛИ Слева направо
?: Условная Справа налево
= *= /= %= += -= <<= >>= &= |= ^= Простое и составное присваивание Справа налево
, Последовательное вычисление Слева направо

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

Выражение может содержать несколько операций одного приоритета. Когда несколько операций одного и того же уровня приоритета появляются в выражении, то они применяются в соответствии с их ассоциативностью — либо справа налево, либо слева направо.

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

а = b & 0xFF + 5

вычисляется как

а = b & (0xFF + 5),

а выражение

а +с >> 1

вычисляется как

(а + с) >> 1

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

В СП ТС реализована операция унарного плюса, позволяющая гарантировать порядок вычисления выражений в скобках.

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

Условная операция вычисляет сначала свой первый операнд, а затем, в зависимости от его значения, либо второй, либо третий.

Логические операции также обеспечивают вычисление своих операндов слева направо. Однако логические операции вычисляют минимальное число операндов, необходимое для определения результата выражения. Таким образом, второй операнд выражения может вообще не вычисляться.

Пример:

int х, у, z, f();

z = х > у || f(x, у);

Сначала вычисляется выражение х>у. Если оно истинно, то переменной z присваивается значение 1, а функция f не вызывается. Если же значение х не больше у, то вычисляется выражение f(x,y). Если функция f возвращает ненулевое значение, то переменной z присваивается 1, иначе 0. Отметим также, что при вызове функции f гарантируется, что значение ее первого аргумента больше второго.

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

if(!feof(pf)) && (с = getc(pf)) …

Здесь feof — функция проверки на конец файла, getc — функция чтения символа из файла (см. раздел 12).

В-третьих, можно гарантировать, что в выражении f(x)&&g(y) функция f будет вызвана раньше, чем функция g. Для выражения f(x)+g(y) этого утверждать нельзя.

В последующих примерах показано группирование операндов для различных выражений.

Выражение Группирование операндов
a & b || c (a & b) || c
a = b || c a = (b || c)
q && r || s-- (q && r) || (s--)
p == 0 ? p += 1 : p += 2 (p == 0 ? p += 1 : p) += 2

В первом примере поразрядная операция И (&) имеет больший приоритет, чем -логическая операция ИЛИ (||), поэтому выражение а&b является первым операндом логической операции ИЛИ.

Во втором примере логическая операция ИЛИ (||) имеет больший приоритет, чем операция простого присваивания, поэтому выражение b||с образует правый операнд операции присваивания. (Обратите внимание на то, что значение, присваиваемое а, есть нуль или единица.)

В третьем примере показано синтаксически корректное выражение, которое может выработать неожиданный результат. Логическая операция И (&&) имеет более высокий приоритет, чем логическая операция ИЛИ (||), поэтому запись q&&r образует операнд. Поскольку логические операции сначала вычисляют свой левый операнд, то выражение q&&r вычисляется раньше, чем s--. Однако если q&&r дает ненулевое значение, то s-- не будет вычисляться и s не декрементируется. Более надежно было бы поместить s-- на место первого операнда выражения либо декрементировать s отдельной операцией.

В четвертом примере показано неверное выражение, которое приведет к ошибке при компиляции. Операция равенства (==) имеет наибольший приоритет, поэтому p==0 группируется в операнд. Тернарная операция ?: имеет следующий приоритет. Ее первым операндом является выражение p==0, вторым операндом — выражение p+=1. Однако последним операндом тернарной операции будет считаться p, а не p+=2. так как в данном случае идентификатор p по приоритету операций связан более тесно с тернарной операцией, чем с составной операцией сложения с присваиванием. В результате возникает синтаксическая ошибка, поскольку левый операнд составной операции присваивания не является L-выражением.

Чтобы предупредить ошибки подобного рода и сделать программу более наглядной, рекомендуется использовать скобки. Предыдущий пример может быть корректно оформлен следующим образом:

(р == 0) ? (р += 1) : (р += 2)

Оглавление книги


Генерация: 1.247. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз