Материал: lectures_3

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

a + b * c

В этом выражении используются два оператора: + (сложить) и * (умножить). Оператор умножить имеет более высокий приоритет по сравнению с оператором сложить. Это следует из таблицы приоритетов, приведенных выше. В рассматриваемом примере вначале будет выполнено умножение переменных b и c, а затем - сложение полученного результата со значением переменной a и полученное значение станет значением всего рассматриваемого выражения.

Пример 2. Рассмотрим выражение, в котором операторы имеют одинаковый приоритет:

a * b / c

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

14.3.Побочные эффекты и вычисления выражений

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

Обратимся к приведенной выше инструкции присваивания

y = a * b;

Побочный эффект здесь состоит в изменении операнда “y”, которому присваивается новое значение, равное значению выражения a * b.

Результат вычислений, предусмотренный выражением, может использоваться двумя способами:

в виде значения выражения,

с помощью побочного эффекта.

Основным способом является использование значения выражения. В процессе выполнения программы выражение замещается вычисленным значением.

Операторы инкремента и декремента рассматриваются в п. 1.15.1. Там же обсуждаются и побочные эффекты при их выполнении.

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

Перейдем к выяснению существа рассматриваемой проблемы. Пусть имеется некоторая произвольная математическая функция y = f(x).

26

Многократные вычисления этой функции для фиксированного значение аргумента x дают всегда один и тот же результат. Причем на этот результат не влияют вычисления других математических функций. Иначе обстоит дело при использовании функций языка Си. Для иллюстрации сказанного приведем пример, в котором используются две библиотечные функции: srand() и rand(), прототипы которых имеют следующий вид:

#include<stdlib.h>

void srand(unsigned seed);

int rand(void);

Функции srand() и rand() применяются для получения так называемой псевдослучайной последовательности чисел. Функция rand() выполняет основную работу по формированию такой последовательности. Для получения последовательности, состоящей из n чисел, необходимо n раз вызвать эту функцию. Функцию srand() следует предварительно вызвать для формирования каждой новой последовательности псевдослучайных чисел. Вызов этой функции используется для инициализации генератора псевдослучайных чисел. Инициализация генератора одним и тем же значением аргумента функции srand() приводит к созданию идентичных псевдослучайных последовательностей. Последовательность чисел, сгенерированная функцией rand() без инициализации генератора, идентична полученной при инициализации с помощью функции srand() со значением аргумента, равным 1. Ниже приведен программный код, с помощью которого формируются две числовые последовательности, состоящие каждая из трех чисел.

srand(10);

printf("%-20s%5d", "The first series: ", rand() % 100); printf("%5d", rand() % 100);

printf("%5d\n", rand() % 100); srand(50);

printf("%-20s%5d", "The second series: ", rand() % 100); printf("%5d", rand() % 100);

printf("%5d\n", rand() % 100);

Замечание 1. В выражении rand() % 100 используется оператор “вычисления остатка” от деления, использующий знак %. Этот оператор вычисляет остаток от деления левого операнда на правый.

Замечание 2. Текстовые литералы: “The first series ” и “The second series” выводятся с использованием выравнивания по левой границе отведенного для каждого поля шириной в 20 позиций. Для этого в спецификации им соответствующей используется модификатор минус (-). При выводе чисел используется принятое по умолчанию выравнивание по правой границе поля.

В результате выполнения этого фрагмента кода в системе

программирования

Dev

C++

version

4

были получены две

последовательности псевдослучайных чисел:

 

 

The first series:

72

99

71

 

 

The second series:

1

51

10

 

 

27

Анализ полученных результатов позволяет сделать следующие выводы: ● Значения, возвращаемые функцией rand(), в каждой из последовательностей зависят от аргумента, передаваемого функции

srand().

● Функция rand() не имеет параметров. Несмотря на это при каждом ее очередном вызове возвращается новое значение.

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

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

int n; printf(“n=”);

scanf(“%d”, &n);

Побочный эффект при выполнении вызова функции printf(), состоит в выводе на экран дисплея наводящего сообщения (n =), а побочный эффект при выполнении функции scanf() состоит в изменении значения переменной n.

При анализе побочных эффектов важно знать, когда они завершаются. Программные точки, в которых гарантированно заканчиваются побочные эффекты, называются точками следования (sequence point). Точки следования имеют место:

в конце каждого выражения, не являющегося подвыражением,

после вычисления первого операнда следующих операторов: “&&” (Логическое И), “||” (Логическое ИЛИ), “?: “ (Вычисление выражения по условию), “,” (оператор запятая).

после вычисления всех параметров функции перед началом выполнения тела функции.

в конце выражений, управляющих работой инструкций: if, switch, while, do..while и в конце каждого из трех выражений в инструкции for.

14.4.Порядок вычисления выражений

Под порядком вычисления (order of evaluation) понимается порядок, в котором вычисляются значения отдельных членов выражения. В языке Си имеется небольшое количество операторов, для которых определен порядок вычисления операндов. К числу таких операторов относятся:

логическое И (&&),

логическое ИЛИ (||),

тернарный оператор (?:),

оператор запятая (,).

Для других операторов очередность вычисления операндов не определена. Например, при анализе выражения sin(x) + cos(x) нельзя

28

ожидать, что вызов функции sin(x) будет выполнен до вызова функции cos(x).

15.Арифметические операторы и выражения

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

любой числовой тип.

Оператор

Назначение

Пример

+

Унарный плюс

+a

-

Унарный минус

-a

++

Две разновидности инкремента

++a (префиксный инкремент)

 

 

 

a++ (постфиксный инкремент)

--

Две разновидности декремента

--a (префиксный декремент)

 

Бинарное сложение

a-- (постфиксный декремент)

+

a + b

-

Бинарное вычитание

a – b

*

Умножение

a * b

/

Деление

 

a / b

%

Остаток от деления нацело

i % n

15.1.

Унарные операторы

 

Остановимся на отдельных операторах. Отметим, что унарный оператор “+” относится к категории программистских недоразумений. Этот оператор никаких полезных действий не выполняет. Дело в том, что значение выражения, содержащего этот оператор, совпадает со значением его операнда. Выражение, содержащее унарный оператор “-”, возвращает значение, отличающееся от операнда только знаком.

Особое место среди унарных операторов занимают операторы инкремента (++) и декремента (--). Операнд, к которому применяются эти операторы, должен относиться к категории lvalue. Особенность этих операторов связана с наличием при их выполнении побочного эффекта. Побочный эффект состоит в изменении значения операнда на 1. Причем для инкремента значение его операнда увеличивается на 1, а для декремента - уменьшается на 1. Оба оператора существуют в двух формах: префиксной и постфиксной формах. Форма оператора не влияет на побочный эффект, имеющий место при выполнении оператора, а определяет возвращаемое оператором значение. При префиксной форме оператор возвращает измененное значение операнда, а при использовании постфиксной формы возвращается первоначальное значение операнда.

Приведем пример. Пусть имеется следующий фрагмент программы.

29

/* ..........................

*/

int n = 5;

 

int m = 5;

 

printf(“++n=%d\n”, ++n);

 

printf(“=%d\n”, n);

 

printf(“m++=%d\n”, m++);

 

printf(“m=%d\n”, m);

 

/* ...........................

*/

В результате выполнения рассматриваемого фрагмента программы вывод на экран дисплея будет иметь следующий вид:

++n=6 n=6

m++=5

m=6

В рассматриваемом примере к переменным “n” и “m”, имеющим равные значения, были применены различные формы инкремента. К переменной “n” была применена префиксная форма инкремента, а к переменной “m” – постфиксная форма. Окончательные значения рассматриваемых переменных оказались одинаковыми. Значения, возвращаемые в результате вычисления выражений ++n и m++, оказались разными. Выражение ++n вернуло новое значение своего операнда (с учетом побочного эффекта), а выражение m++ вернуло исходное значение своего операнда.

15.2.Бинарные операторы

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

следующего вызова функции printf()

printf(“23 / 4 = %d -23 / 4= %d\n”, 23 / 4, -23 / 4);

При выполнении программы, содержащий этот вызов функции, в среде

Builder v. 6 был получены следующие результаты:

23 / 4= 5 -23 / 4= - 5.

Отметим, что при выполнении в другой среде при вычислении выражения -23 / 4 может быть получено число -6.

Бинарный оператор “%” выполняет вычисление остатка от деления левого операнда на правый. Каждый из операндов может иметь любой

30

Источник: https://studfile.net/preview/16544272/