- •Введение
- •Язык программирования Си Элементы языка программирования.
- •Множества символов
- •Буквы и цифры
- •Пробельные символы
- •Знаки пунктуации и специальные символы
- •Операции
- •Константы
- •Целые константы
- •Константы с плавающей точкой
- •Константа-символ
- •Строковые литералы
- •Идентификаторы
- •Ключевые слова
- •Комментарии
- •Лексемы
- •Структура программы
- •Исходная программа
- •Исходные файлы
- •Выполнение программ
- •Время жизни и видимость
- •Классы имен
- •Объявления
- •Спецификаторы типов
- •Область значений величин
- •Деклараторы
- •Деклараторы массивов, функций и указателей
- •Составные деклараторы
- •Объявления переменной
- •Объявление простой переменной
- •Объявление перечисления
- •Объявления структур
- •Объявление совмещений
- •Объявление массива
- •Объявление указателей
- •Объявление функций
- •Объявление классов
- •Классы памяти
- •Объявления переменной на внешнем уровне
- •Объявление переменной на внутреннем уровне
- •Объявление функции на внешнем и внутреннем уровнях
- •Инициализация
- •Базовые типы и типы указателей
- •Составные типы
- •Строковые инициализаторы
- •Объявления типов
- •Типы структур, совмещений и перечислений
- •Объявления typedef
- •Имена типов
- •Выражения и присваивания
- •Введение
- •Операнды
- •Константы
- •Идентификаторы
- •Вызовы функций
- •Индексные выражения
- •Выражение выбора структурного элемента
- •Выражения с операциями
- •Выражения в скобках
- •Type-cast выражения
- •Константные выражения
- •Операции
- •Обычные арифметические преобразования.
- •Операции дополнения
- •Операция адресации и разадресации
- •Операция sizeof
- •Мультипликативные операции
- •Аддитивные операции
- •Операции сдвига
- •Операции отношений
- •Побитовые операции
- •Логические операции
- •Операция последовательного вычисления
- •Условная операция
- •Операции присваивания
- •Lvalue-выражения
- •Унарные инкремент и декремент
- •Простое присваивание
- •Составное присваивание
- •Старшинство и порядок выполнения
- •Побочные эффекты
- •Преобразования типов
- •Преобразование типов при присваивании
- •Преобразования type-cast
- •Преобразования, выполняемые операциями
- •Преобразования при вызовах функций
- •Операторы
- •Введение
- •Оператор break
- •Составной оператор
- •Оператор continue
- •Оператор do
- •Оператор-выражение
- •Оператор for
- •Goto и помеченные операторы
- •Оператор if
- •Оператор null
- •Оператор return
- •Оператор switch
- •Оператор while
- •Функции
- •Введение
- •Определение функции
- •Класс памяти
- •Тип возврата
- •Формальные параметры
- •Тело функции
- •Объявления функции
- •Вызовы функций
- •Фактические аргументы
- •Вызовы с переменным числом аргументов
- •Рекурсивные вызовы
- •Директивы препроцессора и указания компилятору
- •Поименованные константы и макросы
- •Директива # define
- •Директива #undef
- •#Include файлы
- •Условная компиляция
- •Директивы #if, #elif, #else, #endif
- •Директивы #ifdef и #ifndef
- •Управление нумерацией строк
Старшинство и порядок выполнения
На старшинство и порядок выполнения операций Си влияют способы группирования и выполнения операндов в выражениях. Старшинство оперций имеет смысл только при наличии нескольких операций, имеющих разные приоритеты. Выражения с более приоритетными операциями вычисляются первыми. В Таблица 5-12. приведены старшинство и порядок выполнения оперций в Си. Старшинство операций уменьшается сверху вниз. Операции, расположенные в одной строке таблицы или объединенные в группу имеют одинаковое старшинство и одинаковый порядок выполнения.
Операция |
Вид операции |
Порядок выполнения |
()[].-> |
Выражение |
Слева направо |
-~!* &++--sizeof |
Унарный |
Справа налево |
*/% |
Мультипликативный |
Слева направо |
+- |
Аддитивный |
Слева направо |
<< >> |
Сдвиг |
Слева направо |
<> <= >= |
Отношение (неравенство) |
Слева направо |
== != |
Отношение (равенство) |
Слева направо |
& |
Побитовое И |
Слева направо |
^ |
Побитовое исключающее ИЛИ |
Слева направо |
| |
Побитовое включающее ИЛИ |
Слева направо |
&& |
Логическое И |
Слева направо |
|| |
Логическе ИЛИ |
Слева направо |
?: |
Условная |
Справа налево |
= *= /= %=+= -= <<= >>=&= |= ^= |
Простое и составное присваивание |
Справа налево |
, |
Последовательное преобразование |
Слева направо |
Таблица 5‑12 Старшинство и порядок выполнения операций в Си
Из Таблица 5-12 следует, что операнды, состоящие из константы, идентификатора, строки, вызова функций, индексного выражения, выражения выбора элементов или скобочного выражения имеют высший приоритет и выполняются слево направо. Преобразование type-cast имеет то же самое старшинство и порядок выполнения как и унарные операторы. Выражения могут содержать различные операции одного старшинства. Когда несколько операций одного и того же уровня старшинства появляется в выражении, то они отрабатываются в соответствии с порядком их выполнения либо справа налево либо слева направо. Результат вычисления выражения, включающего несколько операций одного и того же старшинства, не зависит от порядка вычисления для операций умножения, сложения и побитовых операций. Компилятор может вычислять такие выражения в любом порядке, даже в случае, когда в выражении появляются скобки, специфицирующие порядок вычисления.
Важно: Только операция последовтельного вычисления (,) и логические операции И (&&) и ИЛИ (||) обеспечивают определенный порядок вычисления операндов. Операция последовательного вычисления (,) обеспечивает преобразование своих операндов слева направо. (Заметим, что запятая, разделяющая аргументы в вызове функции, не является операцией последовательного вычисления и не обеспечивает таких гарантий.)
Логические операции также обеспечивают вычисление своих операндов слева направо. Однако логические операции вычисляют минимальное число операндов, необходимое для определения результата выражения. Таким образом, некоторые операнды выражения могут быть не вычислены. Например, в выражении x && y ++ второй операнд y ++ вычисляется только тогда, когда x есть true (не нуль). Так что y не инкрементируется, когда x есть false (нуль).
В нижеследующих примерах показано группирование по умолчанию для различных выражений.
Выражение |
Группирование по умолчанию |
a & b || c |
(a & b) || c |
a = b || c |
a = (b || c) |
q && r || s-- |
(q && r) || s-- |
В первом примере побитовая операция И (&) имеет большее старшинство, чем логическая операци ИЛИ (||), поэтому выражение a & b является первым операндом логической операции ИЛИ.
Во втором примере логическая операция ИЛИ (||) имеет большее старшинство, чем операция простого присваивания, поэтому выражение b || c представляет правый операнд в присваивании. Заметим, что значение , присваиваемое a, есть нуль или единица.
В третьем примере показано корректно оформленное выражение, которое может выработать неожиданный результат. Логическая операция И (&&) имеет более высокое старшинство, чем логическая операция ИЛИ (||), поэтому запись q && r группируется в операнд. Так как логические операции обеспечивают вычисление операндов слева направо, то выражение q && r вычисляется раньше, чем s--. Однако, если q && r выработает ненулевое значение, то s-- не будет вычисляться и s не декрементируется. Чтобы корректно решить эту проблему, необходимо чтобы s-- появилось в качестве первого операнда выражения, либо декрементировано отдельной операцией.
В следующем примере показано неверное выражение, которое вырабатывает программную ошибку.
Неверное выражение |
Группирование по умолчанию |
p == 0 ? p += 1 : p += 2 |
(p == 0 ? p += 1 : p) += 2 |
В этом примере операция эквивалентности (==) имеет наибольшее старшинство, поэтому p == 0 группируется в качестве операнда. Тернарный оператор (?:) имеет следующее старшинство. Его первым операндом является выражение p ==0, вторым операндом является выражение p +=1. Однако, последним операндом тернарной операции будет рассмотрен p, а не p += 2, так как в данном случае p по старшинству операций связан более тесно с тернарной операцией, а не с сотавной операцией присваивания. В результате будет выработана синтаксическая ошибка, поскольку += 2 не имеет левого операнда (Lvalue-выражения).
Чтобы предупредить ошибки подобного рода и сделать программу более наглядной, рекомендуется использовать скобки. Предыдущий пример может быть корректно офрмлен следующим образом:
(p == 0) ? (p += 1) : (p += 2)