Чем отличаются в языке си выражение и оператор
Лекция 3. Операторы и выражения¶
Архитектура программы¶
Выражения и операторы¶
Понятие оператора¶
Существуют операторы преобразования данных и операторы управления работой программы.
Операции¶
Характеристики операций¶
Приоритет операций¶
Результат операций¶
В языке Си у операций могут быть следующие результаты:
Арифметические операции¶
Знак | Операция |
| Умножение |
/ | Деление и целочисленное деление нацело |
% | Деление по модулю и остаток от деления |
| Сложение |
| Вычитание |
Знак / всегда означает деление. Однако если с обеих сторон от этого знака стоят целые величины (константы, переменные или их комбинации), он означает целочисленное деление. Если в результате такого деления получается остаток, С++ его отбрасывает.
Знак % означает выделение остатка при целочисленном делении. Эта операция требует, чтобы с обеих сторон от ее знака стояли целые величины
Присваивание¶
Особенностью Си является возможность комбинирования операции присваивания с другими операциями, а также выполнение множественного присваивания.
Существуют два класса выражений:
l-value (левостороннее выражение. Может стоять слева от присваивания)
r-value (правостороннее выражение. Может стоять справа от присваивания)
Составное присваивание¶
Эта группа операций позволяет совместить арифметику и присваивание, что дает выразительность и удобочитаемость программам.
Преобразование типов¶
В различных выражениях могут встречаться данные как одного, так и разных типов. Компилятор может выполнять операцию приведения типов по-умолчанию, действуя согласно простого правила: короткие типы приводятся к длинным. Если необходимо изменить стандартное преобразование, то вводят явное приведение типов.
Сравнение¶
Операции сравнения являются бинарными и используются для сравнения двух значений
Значения операций¶
Пример выражения | Результат |
-4+6 | 2 |
c=3+8 | 11 |
5>3 | 1 |
6+(c=3+8) | 17 |
6+c=3+8 | Ошибка! |
Инкремент и декремент¶
будет эквивалентен следующему
будет эквивалентен следующему
Операции инкремента/декремента¶
Что можно сказать о следующей программе?
Ошибка компиляции: l-value required as left operand of assignment
Неопределенное поведение¶
Определение (см. Википедию)
Неопределённое поведение (англ. undefined behaviour) — свойство некоторых языков программирования (наиболее заметно в Си), программных библиотек и аппаратного обеспечения в определённых маргинальных ситуациях выдавать результат, зависящий от реализации компилятора (библиотеки, микросхемы) и случайных параметров
При его выполнении переменная i может принять значения 13 или 14 для C/C++, 13 для Java, PHP и C#, 12 при реализации на LISP.
Неопределенность в языке C/C++ связана с тем, что согласно стандартам С и С++ побочные эффекты (то есть инкремент в данном случае) могут быть применены в любой удобный для компилятора момент между двумя точками следования (см. раздел Дополнительная информация).
Операторы¶
Основные алгоритмические конструкции¶
Основная теорема структурного программирования
Программа для решения любой задачи может быть составлена из комбинации следования, ветвления и цикла (Бойм-Якопини, 1966).
Классификация операторов¶
Оператор if¶
Оператор if¶
Примеры if¶
Несколько версий одной и той же программы
Удачные/неудачные конструкции¶
Удачные и неудачные конструкции¶
Среди всех приведенных конструкций наиболее удачной следует признать последнюю, так как она лишена нагромождений и воспринимается однозначно.
Если необходимо сравнить значение переменной с набором констант, то лучше отказаться от if в пользу switch
Сложные выражения¶
С позволяет конструировать очень сложные выражения. Эта сложность должна быть оправдана.
Оператор switch¶
Оператор switch¶
Вопрос: Чему будут равны значения переменных, если строка: babah!?
Оператор while¶
Операторы цикла¶
Оператор while¶
Ошибки при организации while¶
В чем состоит ошибка?
Оператор do while¶
Оператор do while¶
Это цикл с постусловием. Тело цикла выполняется как минимум 1 раз
Оператор for¶
Оператор for¶
Оператор for¶
Операторы управления¶
Оператор goto усложняет отладку программы и сильно портит стиль разработчика (‘’спагетти-код’‘)!
Спагетти-код¶
Пример спагетти кода на языке BASIC:
Тоже самое, но без goto:
Оператор break¶
Оператор continue¶
Дополнительная информация¶
Точка следования (англ. Sequence point) — в программировании любая точка программы, в которой гарантируется, что все побочные эффекты предыдущих вычислений уже проявились, а побочные эффекты последующих еще отсутствуют.
В C и C++ определены следующие точки следования:
Чем отличаются в языке си выражение и оператор
Все операторы языка СИ могут быть условно разделены на следующие категории:
— условные операторы, к которым относятся оператор условия if и оператор выбора switch;
— операторы цикла (for,while,do while);
— операторы перехода (break, continue, return, goto);
— другие операторы (оператор «выражение», пустой оператор).
Операторы в программе могут объединяться в составные операторы с помощью фигурных скобок. Любой оператор в программе может быть помечен меткой, состоящей из имени и следующего за ним двоеточия.
Все операторы языка СИ, кроме составных операторов, заканчиваются точкой с запятой «;».
1.4.1. Оператор выражение
Любое выражение, которое заканчивается точкой с запятой, является оператором.
Выполнение оператора выражение заключается в вычислении выражения. Полученное значение выражения никак не используется, поэтому, как правило, такие выражения вызывают побочные эффекты. Заметим, что вызвать функцию, невозвращающую значения можно только при помощи оператора выражения. Правила вычисления выражений были сформулированы выше.
Этот оператор представляет выражение, которое увеличивает значение переменной i на единицу.
Этот оператор представляет выражение, включающее в себя операции присваивания и вызова функции.
Этот оператор представляет выражение состоящее из вызова функции.
1.4.2. Пустой оператор
Пустой оператор состоит только из точки с запятой. При выполнении этого оператора ничего не происходит. Он обычно используется в следующих случаях:
— в операторах do, for, while, if в строках, когда место оператора не требуется, но по синтаксису требуется хотя бы один оператор;
— при необходимости пометить фигурную скобку.
Синтаксис языка СИ требует, чтобы после метки обязательно следовал оператор. Фигурная же скобка оператором не является. Поэтому, если надо передать управление на фигурную скобку, необходимо использовать пустой оператор.
1.4.3. Составной оператор
Составной оператор представляет собой несколько операторов и объявлений, заключенных в фигурные скобки:
Заметим, что в конце составного оператора точка с запятой не ставится.
Выполнение составного оператора заключается в последовательном выполнении составляющих его операторов.
Переменные e,g,f,q будут уничтожены после выполнения составного оператора. Отметим, что переменная q является локальной в составном операторе, т.е. она никоим образом не связана с переменной q объявленной в начале функции main с типом int. Отметим также, что выражение стоящее после return может быть заключено в круглые скобки, хотя наличие последних необязательно.
1.4.4. Оператор if
if (выражение) оператор-1; [else оператор-2;]
Выполнение оператора if начинается с вычисления выражения.
Далее выполнение осуществляется по следующей схеме:
— если выражение истинно (т.е. отлично от 0), то выполняется оператор-1.
— если выражение ложно (т.е. равно 0),то выполняется оператор-2.
— если выражение ложно и отсутствует оператор-2 (в квадратные скобки заключена необязательная конструкция), то выполняется следующий за if оператор.
После выполнения оператора if значение передается на следующий оператор программы, если последовательность выполнения операторов программы не будет принудительно нарушена использованием операторов перехода.
Этот пример иллюстрирует также и тот факт, что на месте оператор-1, так же как и на месте оператор-2 могут находиться сложные конструкции.
Допускается использование вложенных операторов if. Оператор if может быть включен в конструкцию if или в конструкцию else другого оператора if. Чтобы сделать программу более читабельной, рекомендуется группировать операторы и конструкции во вложенных операторах if, используя фигурные скобки. Если же фигурные скобки опущены, то компилятор связывает каждое ключевое слово else с наиболее близким if, для которого нет else.
В результате выполнения этой программы r станет равным 2.
Если же в программе опустить фигурные скобки, стоящие после оператора if, то программа будет иметь следующий вид:
В этом случае r получит значение равное 3, так как ключевое слово else относится ко второму оператору if, который не выполняется, поскольку не выполняется условие, проверяемое в первом операторе if.
Следующий фрагмент иллюстрирует вложенные операторы if:
Из рассмотрения этого примера можно сделать вывод, что конструкции использующие вложенные операторы if, являются довольно громоздкими и не всегда достаточно надежными. Другим способом организации выбора из множества различных вариантов является использование специального оператора выбора switch.
1.4.5. Оператор switch
Оператор switch предназначен для организации выбора из множества различных вариантов. Формат оператора следующий:
Выражение, следующее за ключевым словом switch в круглых скобках, может быть любым выражением, допустимыми в языке СИ, значение которого должно быть целым. Отметим, что можно использовать явное приведение к целому типу, однако необходимо помнить о тех ограничениях и рекомендациях, о которых говорилось выше.
Значение этого выражения является ключевым для выбора из нескольких вариантов. Тело оператора smitch состоит из нескольких операторов, помеченных ключевым словом case с последующим константным-выражением. Следует отметить, что использование целого константного выражения является существенным недостатком, присущим рассмотренному оператору.
Так как константное выражение вычисляется во время трансляции, оно не может содержать переменные или вызовы функций. Обычно в качестве константного выражения используются целые или символьные константы.
Все константные выражения в операторе switch должны быть уникальны. Кроме операторов, помеченных ключевым словом case, может быть, но обязательно один, фрагмент помеченный ключевым словом default.
Список операторов может быть пустым, либо содержать один или более операторов. Причем в операторе switch не требуется заключать последовательность операторов в фигурные скобки.
Отметим также, что в операторе switch можно использовать свои локальные переменные, объявления которых находятся перед первым ключевым словом case, однако в объявлениях не должна использоваться инициализация.
Схема выполнения оператора switch следующая:
— вычисляется выражение в круглых скобках;
— вычисленные значения последовательно сравниваются с константными выражениями, следующими за ключевыми словами case;
— если одно из константных выражений совпадает со значением выражения, то управление передается на оператор, помеченный соответствующим ключевым словом case;
— если ни одно из константных выражений не равно выражению, то управление передается на оператор, помеченный ключевым словом default, а в случае его отсутствия управление передается на следующий после switch оператор.
Отметим интересную особенность использования оператора switch: конструкция со словом default может быть не последней в теле оператора switch. Ключевые слова case и default в теле оператора switch существенны только при начальной проверке, когда определяется начальная точка выполнения тела оператора switch. Все операторы, между начальным оператором и концом тела, выполняются вне зависимости от ключевых слов, если только какой-то из операторов не передаст управления из тела оператора switch. Таким образом, программист должен сам позаботится о выходе из case, если это необходимо. Чаще всего для этого используется оператор break.
Для того, чтобы выполнить одни и те же действия для различных значений выражения, можно пометить один и тот же оператор несколькими ключевыми словами case.
Рассмотрим ранее приведенный пример, в котором иллюстрировалось использование вложенных операторов if, переписанной теперь с использованием оператора switch.
Использование оператора break позволяет в необходимый момент прервать последовательность выполняемых операторов в теле оператора switch, путем передачи управления оператору, следующему за switch.
Отметим, что в теле оператора switch можно использовать вложенные операторы switch, при этом в ключевых словах case можно использовать одинаковые константные выражения.
1.4.6. Оператор break
Оператор break обеспечивает прекращение выполнения самого внутреннего из объединяющих его операторов switch, do, for, while. После выполнения оператора break управление передается оператору, следующему за прерванным.
1.4.7. Оператор for
for ( выражение 1 ; выражение 2 ; выражение 3 ) тело
Схема выполнения оператора for:
1. Вычисляется выражение 1.
2. Вычисляется выражение 2.
3. Если значения выражения 2 отлично от нуля (истина), выполняется тело цикла, вычисляется выражение 3 и осуществляется переход к пункту 2, если выражение 2 равно нулю (ложь), то управление передается на оператор, следующий за оператором for.
Существенно то, что проверка условия всегда выполняется в начале цикла. Это значит, что тело цикла может ни разу не выполниться, если условие выполнения сразу будет ложным.
В этом примере вычисляются квадраты чисел от 1 до 9.
Некоторые варианты использования оператора for повышают его гибкость за счет возможности использования нескольких переменных, управляющих циклом.
В этом примере, реализующем запись строки символов в обратном порядке, для управления циклом используются две переменные top и bot. Отметим, что на месте выражение 1 и выражение 3 здесь используются несколько выражений, записанных через запятую, и выполняемых последовательно.
Другим вариантом использования оператора for является бесконечный цикл. Для организации такого цикла можно использовать пустое условное выражение, а для выхода из цикла обычно используют дополнительное условие и оператор break.
Так как согласно синтаксису языка Си оператор может быть пустым, тело оператора for также может быть пустым. Такая форма оператора может быть использована для организации поиска.
Глава 2. Типы, операторы и выражения
Переменные и константы являются основными объектами, с которыми имеет дело программа. Переменные перечисляются в описаниях, где устанавливаются их типы, а возможно, и начальные значения. Операции специфицируют те действия, которые с ними совершаются. Для получения новых значений выражения могут оперировать с переменными и константами. Тип объекта определяет множество значений, которые этот объект может принимать, и операций, которые над ними могут выполняться. Названные «кирпичики» и будут предметом обсуждения в этой главе.
2.1. Имена переменных
Хотя мы ничего не говорили об этом в гл. 1, но существуют некоторые ограничения на задание имен переменных и именованных констант. Имена составляются из букв и цифр; первой литерой должна быть буква. Знак подчеркивания ‘_’ считается буквой; его иногда удобно использовать, чтобы улучшить восприятие длинных имен переменных. Не начинайте имена переменных с подчеркивания, так как многие переменные библиотечных программ начинаются именно с этого знака. Большие (прописные) и малые (строчные) буквы различаются, так что x и X — два разных имени. Обычно в программах на Си малыми буквами набирают переменные, а большими — именованные константы.
Разумно переменным давать осмысленные имена в соответствии с их назначением, причем такие, чтобы их было трудно спутать друг с другом. Мы предпочитаем короткие имена для локальных переменных, особенно для счетчиков циклов, и более длинные для внешних переменных.
2.2. Типы и размеры данных
В Си существует всего лишь несколько базовых типов:
char | единичный байт, который может содержать одну литеру из допустимого набора литер |
int | целое, обычно отображаемое на естественное представление целых в машине |
float | число с плавающей точкой одинарной точности |
double | число с плавающей точкой двойной точности |
Имеется также несколько квалификаторов, которые можно использовать вместе с указанными базовыми типами. Например, квалификаторы short (короткий) и long (длинный) применяются к целым:
В таких описаниях слово int можно опускать, что обычно и делается.
Упражнение 2.1.
2.3. Константы
Некоторые литеры в литерных и стринговых константах записываются с помощью эскейп-последовательностей, например \n (новая_строка); такие последовательности изображаются двумя литерами, но обозначают одну. Кроме того, произвольный восьмеричный код можно задать в виде
где ooo — одна, две или три восьмеричные цифры (0. 7) или
где hh — одна, две или более шестнадцатиричные цифры (0. 9, a. f, A. F). Таким образом, мы могли бы написать
или в шестнадцатиричном виде:
Полный набор эскейп-последовательностей следующий:
\a | сигнал_звонок |
\b | возврат_на_шаг |
\f | перевод_страницы |
\n | новая_строка |
\r | возврат_каретки |
\t | гор_табуляция |
\v | верт_табуляция |
\\ | обратная_наклонная_черта |
\? | знак_вопроса |
\’ | одиночная_кавычка |
\» | двойная_кавычка |
\ ooo | восьмеричный_код |
\x hh | шестнадцатиричный_код |
Константные выражения — это выражения, оперирующие только с константами. Такие выражения вычисляются во время компиляции, а не во время счета, и поэтому их можно использовать в любом месте, где допустимы константы, как, например, в
Стринговая константа, или стринговый литерал, — это нуль или более литер, заключенных в двойные кавычки, как, например,
эквивалентна записи одного следующего стринга:
Указанное свойство позволяет разбивать длинные стринги на части и располагать эти части на отдельных строчках.
В Си имеется еще один вид константы, константа перечисления. Перечисление — это список целых констант, как, например, в
Первое имя в enum (от английского слова enumeration — перечисление. — Примеч-ред.) имеет значение 0, следующее — 1 и т.д. (если не было явных спецификаций значений констант). Если не все значения специфицированы, то они продолжают прогрессию, начиная от последнего специфицированного значения, как в следующих двух примерах:
Имена в различных перечислениях должны отличаться друг от друга. Значения внутри одного перечисления могут совпадать.
2.4. Декларации
Все переменные должны быть декларированы раньше, чем будут использоваться, при этом некоторые декларации могут быть получены неявно — из контекста. Декларация специфицирует тип и содержит список из одной или нескольких переменных этого типа, как, например, в
Переменные можно распределять по декларациям произвольным образом, так что указанные выше списки можно записать и в следующем виде:
Эта последняя форма записи занимает больше места, тем не менее она лучше, поскольку позволяет добавлять к каждой декларации комментарий и более удобна для последующих модификаций.
В своей декларации переменная может быть инициализирована, как, например:
Инициализация неавтоматической переменной осуществляется только один раз — перед тем, как программа начнет выполняться, при этом инициализатор должен быть константным выражением. Явно инициализируемая автоматическая переменная получает начальное значение каждый раз при входе в функцию или блок, ее инициализатором может быть любое выражение. Внешние и статические переменные по умолчанию получают нулевые значения. Автоматические переменные, явным образом не инициализированные, содержат неопределенные значения («мусор»).
К любой переменной в декларации может быть применен квалификатор const для указания того, что ее значение далее не будет изменяться.
Применительно к массиву квалификатор const указывает на то, что ни один из его элементов не будет меняться. Указание const можно также применять к аргументу–массиву, чтобы сообщить, что функция не изменяет этот массив:
2.5. Арифметические операторы
дает остаток от деления x на y и, следовательно, нуль, если x делится на y нацело. Например, год является високосным, если он делится на 4 (но не на 100). Кроме того, високосным считается год, если он делится на 400. Следовательно,
Оператор % к операндам типов float и double не применяется. В какую сторону, (в сторону увеличения или уменьшения числа) будет усечена дробная часть при выполнении / и каким будет знак результата операции % с отрицательными операндами, это зависит от машины.
В конце этой главы приводится табл. 2.1, в которой показаны приоритеты всех операторов и порядок их выполнения.
2.6. Операторы отношения и логические операторы
Операторами отношения являются
Все они имеют одинаковый приоритет. Ровно на одну ступень ниже приоритет операторов сравнения на равенство:
По определению численным результатом вычисления выражения отношения или логического является 1 в случае, если оно истинно, и 0 в случае, если оно ложно.
Упражнение 2.2.
2.7. Преобразования типов
Как мы уже говорили в гл. 1, выражение
В случае ASCII эта программа будет правильно работать потому, что между одноименными буквами верхнего и нижнего регистров — одинаковое расстояние (если их рассматривать как числовые значения), и, кроме того, латинский алфавит — плотный в том смысле, что между буквами A и Z ничего кроме букв не существует. Для набора EBCDIC последнее условие не выполняется, и поэтому наша программа в этом случае будет преобразовывать не только буквы.
Существует одна тонкость, касающаяся преобразования литер в целые: язык не определяет, являются ли переменные типа char знаковыми или беззнаковыми. При преобразовании char в int может когда-нибудь получиться отрицательное целое? На машинах с разной архитектурой ответы могут отличаться. На некоторых машинах значение типа char с единичным старшим битом будет превращено в отрицательное целое (посредством «размножения знака»). На других — преобразование char в int осуществляется добавлением нулей слева, и, таким образом, получаемое значение всегда положительно.
Неявные арифметические преобразования, как правило, осуществляются естественным образом. В общем случае, когда оператор типа + или * с двумя операндами (бинарный оператор) имеет разнотипные операнды, прежде чем операция начнет выполняться, «младший» тип подтягивается к «старшему». Результат будет иметь старший тип. В разд. 6 приложения A правила преобразования сформулированы точно. Если же в выражении нет беззнаковых операндов, можно удовлетвориться следующим набором неформальных правил:
Преобразования имеют место и при присваиваниях: значение правой части присваивания приводится к типу левой части, который и является типом результата.
Литера превращается в целое посредством размножения знака или другим описанным выше способом.
Длинные целые преобразуются в короткие целые или в значения типа char с помощью отбрасывания старших разрядов. Так, в
значение c не изменится. Это справедливо независимо от того, размножается знак при переводе char в int или нет. Однако, если изменить порядок присваиваний, возможна потеря информации.
И наконец, для любого выражения можно явно указать преобразование его типа, используя унарный оператор, называемый приведением. Конструкция вида
В том случае, когда аргументы описаны в прототипе функции, как тому и следует быть, при вызове функции нужное преобразование включается автоматически. Так, при наличии прототипа функции sqrt :
перед обращением к sqrt в присваивании
целое 2 будет переведено в значение double 2.0 автоматически без явного указания операции приведения.
Операцию приведения проиллюстрируем на переносимой версии генератора псевдослучайных чисел и функции, осуществляющей начальную «затравку», входящих в стандартную библиотеку.
Упражнение 2.3.
2.8. Инкрементные и декрементные операторы
установит в x значение 5, а
установит в x значение 6. И в том и другом случае значение n станет равным 6. Инкрементные и декрементные операторы можно применять только к переменным. Например, запись (i+j)++ не верна. В контексте, где требуется только увеличить (или уменьшить) значение переменной, как в
можно переписать более компактно:
Упражнение 2.4.
Упражнение 2.5.
2.9. Побитовые операторы
& | побитовое И |
| | побитовое ИЛИ |
^ | побитовое исключающее ИЛИ |
сдвиг влево | |
>> | сдвиг вправо |
побитовое отрицание (унарный) |
Оператор & (побитовое И) часто используется для обнуления некоторой группы разрядов. Например,
очищает в n все разряды, кроме младших семи.
Оператор | (побитовое ИЛИ) применяют для установки разрядов; так,
Оператор ^ (побитовое исключающее ИЛИ) в каждом разряде установит 1, если соответствующие разряды операндов имеют различные значения, и 0, когда они совпадают.
Операторы и >> выполняют сдвиг, влево или вправо, своего левого операнда на число битовых позиций, задаваемое правым операндом, которое должно быть положительным. Так, x сдвигает значение x влево на 2 позиции, заполняя освобождающиеся биты нулями, что эквивалентно умножению x на 4. Сдвиг вправо беззнаковой величины всегда сопровождается заполнением освобождающихся разрядов нулями. Сдвиг вправо знаковой величины на одних машинах происходит с размножением знака («арифметический сдвиг»), на других — с заполнением освобождающихся разрядов нулями («логический сдвиг»).
производит дополнение целого до единиц по всем разрядам, т.е. превращает единичные биты в нулевые и наоборот. Например,
обнуляет в x последние 6 разрядов. Заметим, что запись x &
077 не потребует дополнительных затрат при счете, так как
077 — константное выражение, которое будет вычислено во время компиляции.
Выражение x >> (p+1-n) сдвигает нужное нам поле к правому краю. Константа
0 состоит только из единиц, и ее сдвиг влево на n бит (
0 приведет к тому, что правый край этой константы займут n нулевых разрядов. Еще одна операция побитового отрицания,
Упражнение 2.6.
Упражнение 2.7.
Упражнение 2.8.
2.10. Операторы присваивания и выражения
в котором стоящая слева переменная повторяется и справа, можно написать в сжатом виде:
Оператор += называется оператором присваивания.
Если выр1 и выр2 — выражения, то запись
с той лишь разницей, что выр1 вычисляется только один раз. Обратите внимание на скобки вокруг выр2: запись
Независимо от машины, на которой будет работать эта программа, описание аргумента x как unsigned гарантирует, что при правом сдвиге освобождающиеся биты будут заполняться нулями, а не знаковым битом.
благодаря оператору присваивания запись становится более легкой для понимания, так как читателю при такой записи не потребуется старательно сравнивать два длинных выражения или выяснять, почему они не совпадают. Следует иметь в виду и то, что оператор присваивания может помочь компилятору сгенерировать более эффективный код.
Мы уже видели, что присваивание вырабатывает значение и может применяться внутри выражения; во многих рядовых программах мы видим
Типом и значением любого выражения присваивания являются тип и значение его левого операнда после завершения присваивания.
Упражнение 2.9.
2.11. Условные выражения
будет float вне зависимости от того, положительно значение n или нет.
Условное выражение часто позволяет сократить программу. В качестве примера приведем цикл, обеспечивающий печать n элементов массива по 10 на каждой строке с одним пробелом между колонками; каждая строка цикла, включая последнюю, заканчивается литерой новая_строка:
Упражнение 2.10.
2.12. Приоритет и порядок вычислений
Таблица 2.1. Приоритеты и порядок вычислений операторов
чтобы получить правильный результат, приходится использовать скобки.
f может быть вычислена раньше g или наоборот. Из этого следует, что если одна из функций изменяет значение переменной, от которой зависит другая функция, то помещаемый в x результат может зависеть от порядка вычислений. Чтобы обеспечить нужную последовательность вычислений, промежуточные результаты можно запоминать во временных переменных.
Порядок вычисления аргументов функции также не определен, поэтому на разных компиляторах
Обращения к функциям, вложенные присваивания, инкрементные и декрементные операторы дают «побочный эффект», проявляющийся в том, что при вычислении выражения значения некоторых переменных изменяются. В любом выражении с побочным эффектом может быть скрыта трудно просматриваемая зависимость результата выражения от порядка изменения значений переменных, входящих в выражение. В такой, например, типично неприятной ситуации —
Мораль такова: писать программы, которые зависят от порядка вычислений, — плохая практика, какой бы язык вы ни использовали. Естественно, надо знать, чего следует избегать, но если вы не знаете, как образуются побочные эффекты на вашей машине, то лучше и не рассчитывать на особенности реализации.