Выражения и операции в языке си icon

Выражения и операции в языке си



НазваниеВыражения и операции в языке си
Дата конвертации16.09.2012
Размер263.61 Kb.
ТипДокументы
1. /Troy/6.doc
2. /Troy/7.doc
3. /Troy/8.doc
4. /Troy/9.doc
Операторы языка си и управление их исполнением
Выражения и операции в языке си
Зачем нужны указатели?
Поля битов и побитовые операции

ГЛАВА 7


ВЫРАЖЕНИЯ И ОПЕРАЦИИ В ЯЗЫКЕ СИ

По мнению авторов языка Си [Ritchie et al. ], в нем предусмотрен богатый набор операций. Действительно, в дополнение к традиционным арифметическим операциям, операциям отношения и присваивания в языке Си предусмотрены сокращенные версии этих операций, побитовые операции и операции над указателями. В этой главе остановимся на традиционной и сокращенной записи операций, а также на конст­рукциях, выражений, включающих эти операции. Операции над указателями расс­мотрим в гл. 8, а побитовые операции в гл. 9

Как обычно, синтаксис и применение этих операций и выражений будут описа­ны в ч. 1 главы. В ч. 2 будет продемонстрировано применение этих операций в программе вычисления выражений.

ЧАСТЬ 1

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







порядке уменьшения их алгебраического приоритета. Кроме того, обсудим правила группировки этих операций.

7.1. ОПЕРАЦИИ ССЫЛКИ

Уже были представлены две из трех операций ссылки (табл. 7.1), а именно опе­рации ссылки на элемент массива и элемент структуры. Здесь приведем более де­тальное описание этих двух операций, а обсуждение операции ссылки с помощью указателя отложим до гл. 8. Операциям ссылки присвоен наивысший приоритет; они группируются слева направо.

Таблица 7.1. Операции ссылки

jpg" name="gray" align=bottom width=518 height=54 border=0>

ССЫЛКА НА ЭЛЕМЕНТ МАССИВА

Операция ссылки на элемент массива предназначена для выделения конкретного элемента массива. Чтобы использовать эту операцию, выражение (называемое индексом и имеющее целое значение) заключают в квадратные скобки и указы­вают при имени массива, например:

array [целое_выражение]





Напомним, что у массива, содержащего N элементов, значения индекса изменя­ются от 0 до N-1. Напомним также, что имя массива, указанное без операции ссылки, означает адрес первого элемента массива. Поэтому можно считать, что алгоритм операции ссылки на элемент массива следующий:

1. Вычислить выражение, которое служит индексом. Предположим, что резуль­тат равен S.

2. Преобразовать S в сдвиг в массиве путем умножения S на размер отдельного элемента массива:

сдвиг = S*sizeof(тип массива).

3. Вычислить адрес элемента массива по формуле

адрес - имя_массива + сдвиг.

4. Извлечь значение, находящееся по этому адресу.

Предположим, к примеру, что массив array имеет тип int. Тогда ссылка array [3] (четвертый элемент) будет найдена как

содержимое адреса(array + (3*sizeof(int)).

При работе с многомерными массивами для каждой размерности необходима своя операция ссылки. Выражение

two_array[выраж_1][выраж_2]

соответствует элементу двумерного массива two_array, находящемуся на пересе­чении строки выраж_1 и столбца выраж_2. Как обсуждалось в гл. 4, элементы массивов упорядочены по строкам. Для массива с размерами N*М это означает следующий порядок расположения элементов:

two_array[0][0] Первый элемент

two_array[0][l] Второй элемент

two_array[0][2] Третий элемент

two_array[l][0] two_array[1][1]

two_array[N-l][M-l] Последний элемент

В действительности операция ссылки на элемент массива может быть использована в любом выражении, результатом вычисления которого является адрес. Если в сле­дующем примере выражение представляет адрес (т.е. имеет тип pointer, см. гл. 8), то ссылка

(выражение) [индекс]

является допустимой в языке Си.

ССЫЛКА НА ЭЛЕМЕНТ СТРУКТУРЫ

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

mystruct.mymember

представляет значение элемента mymember структуры mystruct. Так как структу­ры могут быть вложенными, то это справедливо и для операции ссылки на элемент структуры. Например

bigstruct.smallstruct.amember представляет элемент amember структуры smallstruct, которая, в свою очередь, является элементом структуры bigstruct.

ССЫЛКА НА ЭЛЕМЕНТ СТРУКТУРЫ С ПОМОЩЬЮ УКАЗАТЕЛЯ

Операция -> требуется в том случае, если для ссылки на элемент структуры используется указатель. Указатели на структуры и операция -> обсуждаются в гл. 8.

7.2. УНАРНЫЕ ОПЕРАЦИИ

Те унарные операции, которые будут обсуждаться в данной главе, приведены в табл. 7.2. Унарные операции над указателями и побитовые унарные операции бу­дут описаны в гл. 8 и 9.



Синтаксис применения этих операций в выражении показан на рис. 7.1.

Учтите, что унарные операции - и ! могут быть использованы в любом выражении, в то время как операции увеличения и уменьшения применяются к адресуемым зна­чениям. (К адресуемым значениям относятся ссылки на объект, например имена пере­менных, ссылки на элементы массивов и на элементы структур.)

Унарные операции группируются справа налево. Другими словами, в пос­ледовательности из двух или большего числа унарных операций крайняя пра­вая исполняется первой. Рассмотрим теперь эти операции подробнее.

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

Унарный минус (-) является обычной арифметической операцией изменения знака. Унарный плюс в языке Си отсутствует. Унарный минус может быть исполь­зован в выражении любого арифметического типа.

ЛОГИЧЕСКОЕ ОТРИЦАНИЕ

Операция логического отрицания, !, изменяет значение "истина" на значение "ложь", а значение "ложь" - на значение "истина". Напомним, что значению "истина" соответствует ненулевое целое значение, а значению "ложь" — нуль. По­этому операция логического отрицания изменяет ненулевое значение на нуль, а нуль - на единицу. Результат выполнения логического отрицания имеет тип int.

УВЕЛИЧЕНИЕ И УМЕНЬШЕНИЕ

Унарное увеличение (++) и унарное уменьшение (--) могут предшествовать опе­ранду или следовать за ним (т.е. эти операции могут быть как префиксными, так и постфиксными). Операндом для них может служить только адресуемое значение. Эти операции добавляют или вычитают единицу из значения того объекта, на который указывает адресуемое значение, и присваивают ему полученный резуль­тат. (Таким образом, они выполняют неявное присваивание). Приведем примеры операторов языка Си, содержащих выражения, включающие в себя как обычные; так и префиксные операции и дающие эквивалентные результаты:

Использование префиксного увеличения и уменьшения Обычная форма выражения Сокращенная форма

х = х + 1; ++х;

у = у - 1; --у;

или

х = х + 1; а = array[++х];

а = array[х];

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

Постфиксные операции увеличения и уменьшения осуществляются почти так же. Значение адресуемого объекта по-прежнему увеличивается (или уменьшается)




на 1, а затем этот объект модифицируется. Однако при этом в выражении исполь­зуется старое значение объекта, которое он имел до модификации. Сопоставьте следующий пример с примером применения префиксного увеличения:

Использование постфиксного увеличения

Обычная форма выражения Сокращенная форма

а = array [x]; a = array [х++];

х = х + 1;

Обратите внимание на отличие. При постфиксном увеличении в качестве значения индекса массива берется то значение переменной х, которое она имела до операции увеличения. Тем не менее этой переменной присваивается новое значение.

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

ПРЕОБРАЗОВАНИЕ ТИПА

В языке Си предусмотрено средство для явного изменения типа выражения. Оно не­редко называется заменой типа, и синтаксис его применения следующий: (спецификация_типа) выражение

Значение приведенного выражения будет преобразовано в значение типа спецификация_типа. Спецификация_типа может быть любым служебным сло­вом, задающим спецификацию типа, например int, short, long, float и т.д., и может быть также именем структуры или объединения либо типом указателя (последние два вида объектов обсуждаются в следующих главах). Рассмотрим, к примеру, следующий фрагмент программы:

int length = 11000;

int width = 4;

long area;

area = length*width;

Результат умножения (44000) превышает максимально допустимое для IBM PC значение целого числа типа int (т.е. возникает переполнение, поскольку компилятор "не заглядывает вперед" и не учитывает тип переменной area). Чтобы заставить компилятор при вычислении выражения использовать длинные целые значения, можно применить операцию замены типа:

area = (long) length * (long) width;

Вначале значения переменных length и width будут извлечены и преобразованы в значения типа long, а уже после этого будет выполнено умножение. Диапазон допустимых значений типа long включает в себя требуемый результат. (Примечание: типы переменных length и width не изменяются - операция замены типа выполняется над зна­чениями этих объектов и только для данного выражения.)

Другим распространенным приложением операции" замены типа является преоб­разование типов аргументов при вызове функций. Например, для многих мате­матических функций требуется, чтобы их аргументы были числами с плавающей точкой или имели тип long. Другим примером может служить функция Iseek, вхо­дящая в состав стандартной библиотеки ввода-вывода (эта библиотека обсуждается в гл. 10). Функции Iseek требуются три аргумента, второй из которых должен иметь тип long. Тем не менее вместо этого аргумента можно подставить целую переменную, только при этом в момент вызова надо выполнить операцию замены типа:

int offset, fd;

Iseek (fd, (long) offset.0);

В процессе вызова содержащееся в переменной offset значение будет извлечено и преобразовано в значение типа long (оно будет сохранено в рабочей области памяти), которое будет передано функции Iseek.

Преобразования типа могут выполняться как по возрастанию, так и по убы­ванию типов. Результат преобразования от меньшего типа к большему приводит к тому же самому значению, которое, возможно, будет представлено с большей точностью. Исключение может составить преобразование типа, не имеющего знака (например, char), в больший тип, например int. Если крайний левый бит значения без знака равен единице, то в некоторых средах программирования выполняется расширение знака и в результате получается отрицательное число. Рассмотрим следующий пример:

char с; int x;

с= '\xFF';

х = с;

printf ("х = %d\n",x);

Объекту с присвоен байт, все биты которого равны единице (такой байт имеет шестнадцатеричное представление '\xFF'). Затем это значение присваивается це­лой переменной х. Каким окажется значение переменной х? Некоторые компиляторы интерпретируют старший бит как знак и могут выполнять расширение знака, копируя знаковый бит в старший байт переменной х. Другие могут не выполнять этих действий. Если расширение знака отсутствует, то пере­менной х присваивается значение 255. При расширении знака переменной х будет присвоено значение -1.

Если заменяется больший тип на меньший, то компилятор отбрасывает избыточные данные. При преобразовании значений с плавающей точкой двойной точности в зна­чения с плавающей точкой одинарной точности выполняется такое округление исход­ного значения, при котором результат помещается в объекте одинарной точности. При преобразовании значений с плавающей точкой в целые значения отбрасывается дроб­ная часть. Большие целые значения преобразуются в меньшие целые путем отбра­сывания избыточных старших битов.

ОПЕРАЦИИ SIZEOF

Операция sizeof выполняется на этапе компиляции программы и дает константу, которая равна числу байтов, требуемых для хранения в памяти данного объекта. Объектом может быть имя переменной, массива, структуры или просто спецификация типа. Применение этой операции демонстрировалось в гл. 3. Приве­дем еще один пример использования операции sizeof: int count, iarray[10];

for (count = 0; count < sizeof(iarray)/sizeof(int); count++); printf("iarray[%d] = %d\n",count,iarray[count]);

Заметьте, что результатом операции sizeof (iarray) будет 20, так как в IBM PC для хранения целого значения выделяется два байта. Таким образом, число эле­ментов массива равно размеру памяти, необходимой для хранения массива, поде­ленному на размер памяти, необходимой для хранения одного его элемента.

Обратите также внимание на использование унарной операции ++ для увеличения значения счетчика. Это типичный стиль программирования на языке Си.

Применение операции sizeof всюду, где это возможно, считается хорошим стилем кодирования программы. Если в приведенном примере размер массива iarray будет изменен, то после компиляции программы условие в цикле for оста­нется правильным (конечно, другая альтернатива - определить именованную кон­станту с помощью директивы #define и использовать ее в определении массива и в условии цикла).

7.3. БИНАРНЫЕ ОПЕРАЦИИ

Бинарные операции воздействуют на два выражения. В их простом синтаксисе

выражение binop выражение

binop - одна из бинарных операций, приведенных в табл. 7.3. Далее эти операции разбиваются на группы и обсуждаются в деталях.

Таблица 7.3. Бинарные операции



АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ

К числу арифметических бинарных операций относятся операции *, /, %, + и

- Эти операции задают обычные действия над операндами любого арифметичес­кого типа. В языке Си автоматически выполняется преобразование типов так, как описывается далее. Одна из указанных операций, а именно %, может оказаться для Вас незнакомой. Это операция вычисления модуля, т.е. остатка от деления одного целого числа на другое. Значением бинарного выражения

dividend % divisor

является остаток от деления значения dividend на значение divisor. Если dividend является целым кратным divisor, то результатом будет нуль. Операция взятия мо­дуля выполняется только над целыми операндами (для значений типа float или double она недопустима).

При вычислении выражений, в которые входят арифметические операции, язык Си следует обычным правилам приоритета, согласно которым наивысший приоритет у унарных операций (!, ++, - -, -, (тип) и sizeof), далее следуют опе­рации *, / и %, а за ними + и -". Для изменения порядка выполнения операций, предписанного их приоритетами, могут быть использованы скобки.

При равном приоритете унарные операции группируются справа налево, а бинарные

- слева направо. Порядок вычисления коммутативных операций в языке Си не регла­ментируется. Например, порядок вызовов функции при вычислении выражения




getchar(c) + getchar(c) в языке не определен. Это может вызвать проблемы только при наличии побочных эффектов в процессе вычисления операндов.

Если выражение содержит операнды различных типов, то язык Си выполняет автоматическое преобразование типов (если замена типов явно не указана). Обычно компилятор выполняет такое преобразование путем "продвижения" значений операндов к "наибольшему" типу. Точнее, при вычислении выра­жения язык следует алгоритму, показанному на рис.7.2.

По определению авторов языка Си, операции над числами с плавающей точкой выполняются с двойной точностью [Kernigan и Ritchie ].

(Кроме того, аргументы функции типа float перед вызовом функции преобра­зуются в значения типа double. Значения аргументов типа char перед вызовом функции преобразуются в значения типа int.)

ОПЕРАЦИИ ОТНОШЕНИЯ

Операции отношения <, >, < =,>=, = = и != также должны быть Вам уже зна­комы. Их назначение описано в табл. 7.4.



Приоритет операций отношения меньше, чем у бинарных арифметических опе­раций. Приоритет операций <, >, < = и > = одинаков и выше, чем у операций = = и !=. Таким образом, в сравнении count < sizeof (iarray)/2

скобки указывать не обязательно, поскольку это выражение эквивалентно выра­жению

count < (sizeof (larray)/2)

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

В выражении операции отношения группируются слева направо.

Помните, что результатом выражения, включающего в себя операции отно­шения, может быть или ненулевое значение ("истина"), или нулевое значение це­лого типа ("ложь"). Таким образом, после выполнения присваивания х= count < sizeof (iarray)/2

переменной х будет присвоено значение либо 1, либо 0.

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

ЛОГИЧЕСКИЕ ОПЕРАЦИИ


Показанные в табл. 7.5 операции служат для связывания выражений, включа­ющих в себя операции отношения.



Рассмотрим два выражения: выраж_1 && выраж_2

и

выраж_1  выраж_2

Результаты применения логических операций определены в табл.7.6.



Помните, что ненулевое целое значение представляет логическое значение "истина", а нулевое - "ложь".

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

if (count < sizeof (iarray)/sizeof (int) && iarray [count] > 0) printf("Значение - %d\n", iarray[count]);

вначале будет вычислено отношение count sizeof(iarray)/sizeof(int). Если его значение "ложь", то заранее можно сказать, что и значение всего выражения тоже "ложь". На этом вычисление выражения будет прекращено. Но если значение данного отношения "истина", то будет вычислено также и отношение iarray [count ] > 0, а после этого к двум полученным значениям будет применена операция &&. Логические бинарные операции группируются слева направо.

7.4. ТЕРНАРНАЯ ОПЕРАЦИЯ

Тернарная операция используется для конструирования условных выражений. Ее синтаксис следующий:

выражение_1 ? выражение_2 : выражение_3

Условным выражением называется такое выражение, которое может иметь одно из двух возможных значений. Оно интерпретируется следующим образом: вначале вычисляется выражение_1. Если его значение отлично от нуля ("истина"), то вычисляется выражение_2 (следующее за знаком ?) и его значение принимается за значение условного выражения. Если же выражение_1 имеет нулевое значение ("ложь"), то за значение условного выражения принимается значение выражение_3. В следующих фрагментах кодов показаны два способа присваивания переменной max максимального из двух значений: Действие тернарной операции

max - (current > next) ? current : next; или

if (current > next)

max - current; else

max - next;

Приоритет тернарной операции ниже приоритета логических операций. Эта опе­рация группируется справа налево.

7.5. ОПЕРАЦИИ ПРИСВАИВАНИЯ

Как Вы уже знаете, синтаксис оператора присваивания следующий: Ivalue - выражение

Значением оператора присваивания служит значение выражения, которое присваивается объекту Ivalue. Поэтому операторы присваивания могут быть вло­женными, например:

max =min = 0;

или

if ((sum = sum + count) < max)

Операции присваивания (=) группируются справа налево. Поэтому в первом примере вначале будет выполнено присваивание min = 0 в результате которого значение min станет равным нулю. Затем это же значение будет присвоено переменной max. Во втором примере значением выражения

sum - sum + count

будет значение правой части оператора присваивания, т.е. (sum + count). Это зна­чение сравнивается в операторе if со значением переменной max, в результате чего получается 0 или 1, что и составляет условие оператора if.

В языке Си использование вложенных операторов присваивания широко расп­ространено и представляет особый интерес в связи с приоритетами операций. В частности, приоритет операции присваивания ниже приоритетов операций отно­шения, логических операций или тернарной операции, она находится почти в са­мом конце списка операций (табл. 7.7).



Поэтому в выражении

(sum - sum + count) < max

необходимо указывать скобки, если желательно, чтобы значение оператора присваивания сравнивалось со значением переменной max. Если в этом выражении опустить скобки: sum - sum + count < max

то ввиду соотношений приоритетов вначале выполнится вычисление выражения sum + count, затем оно будет сравнено со значением переменной max. Полученный результат (0 или 1) будет присвоен переменной sum. Неправильный учет приоритетов является одной из распространенных ошибок при программировании на языке Си, поэтому постарайтесь хорошо запомнить соотношения между приоритетами операций.

Вернемся к синтаксису оператора присваивания. Более компактная форма его определения имеет вид

Ivalue asgnop выражение

где asgnop представляет другие операции в дополнение к операции =. В языке Си предусмотрена сокращенная форма записи для выражения вида Ivalue - Ivalue binop выражение

где один и тот же объект находится и справа, и слева от знака операции =, а binop - одна из бинарных операций +, -, «, / или % (см. табл. 7.7; другие операции будут обсуждаться в следующей главе). Эта сокращенная форма имеет вид

Ivalue = binop= выражение

В следующих примерах операторы левой и правой колонок эквивалентны: Обычная форма выражения Сокращенная форма

count = count + 1; count += 1;

offset = offset/2; offset /= 2;

scale = scale*(fac +2); scale *= fac +2;

array[i + 2] = array[i + 2] - 10; array[i + 2] -= 10;

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

ПРИСВАИВАНИЯ МАССИВОВ И СТРУКТУР


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

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

struct STOCK INFO current, old;

old = current;

скопирует все содержимое структуры current в структуру old. Чтобы выяснить, обеспечивает ли Ваш компилятор эту возможность, обратитесь к его докумен­тации.

7.6. ОПЕРАЦИЯ "ЗАПЯТАЯ"

Операция "запятая" может быть использована для разделения нескольких вы­ражений. Типом данных и значением пары выражений, разделенных запятой, являются тип данных и значение последнего выражения. Эта операция чаще всего применяется в операторе for для того, чтобы выполнялось более одного выражения в управляющей части этого оператора. Например, следующий оператор про­суммирует первые 10 ненулевых целых значений: for (c=1, sum=1; с < 10; с++, sum +=c);

Запятая позволяет здесь поместить несколько выражений вместо одного, обуслов­ленного синтаксисом. В приведенном примере на каждом проходе цикла будет вы­полнен как оператор с++, так и оператор sum += с.

Приоритет запятой среди всех операций самый низкий. Эта операция группируется слева направо.

7.7. ЗАКЛЮЧЕНИЕ

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

Наряду с соотношениями приоритетов операций программист должен помнить о том, что в определении языка Си нет никаких ограничений на то, в каком порядке компилятор будет обрабатывать вычисление двух операндов комму­тативной арифметической бинарной операции. Логические операции && и  всегда выполняются слева направо, но при этом вычисление выражения прекращается, как только его результат можно предсказать. Кроме того, в языке Си не I определен порядок вычисления аргументов функции при ее вызове.

ЧАСТЬ 2

7.8. ПРОГРАММА ВЫЧИСЛЕНИЯ ВЫРАЖЕНИЙ

Теперь воспользуемся обсуждавшимися средствами языка Си для модификации программы вычисления выражений. В частности, внесем изменения в модули SCAN.C, EVAL.C и STACK.C. Например, в модуле SCAN.C операторы

if (ch < ' ') return(ER);

If (ch > '9') return(ER);

заменим операторами

If (ch < ' '  ch > '9') return(ER);

В функции getline фрагмент кода

с = getchar();

/* Игнорировать ведущие символы перехода на новую строку */

while (с = = '\n') с = getchar();

/* Считывать символы, пока не будет обнаружен возврат каретки */

while (с != '\n')

{

/* Не переполнять массив -- оставить место для нулевого

байта */

if (i < (size - 1))

{

array[i] = с; i=i+1;

}

с = getchar();

}

можно расширить и сделать более элегантным:

while (((с = getchar()) = = '\n') :: (с = = ' ') :: (с = = '\t'));

while (с != '\n')

/* Не переполнять массив — оставить место для нулевого

байта */

if (i < (size - 1))

{

array [i++] = с;

}

с = getchar();

}

Наконец, два характерных оператора из функции exs_push (модуль STACK.C) ex_index = ex_index + 1; ex_stack[ex_index] = value;

будут переписаны в виде одного оператора ex_stack[++ex_index] = value;

Полный модифицированный код программы вычисления выражений имеет следу­ющий вид:

/* Содержание файла CALC.H */

/* * Определить возвращаемые значения общих функций.

#define TRACE /* Включить в программу отладочный код */

#undef TRACE /* Исключить из программы отладочный код */

#define SUCCESS 1 #define FAILURE 0 #define DONE 0 /*

* Ниже приводятся определения кодов элементов выражения и классов

* символов, используемых Функцией scanner.

* Эти значения используются как индексы в таблице переходов.

*/

#define END 0 /* Конец выражения */

#define EMPTY 0 /* Пустой стек */

#define LPAREN 1 /* ( */

#define RPAREN 6 /* ) */

#define MULT 4 /* * */

#define PLUS 2 /* + */

#define MINUS 3 /* - */

#define DOT 8 /* . (десятичная точка) */

#define DIVIDE 5 /* / */

#define NUMBER 7 /* Числовая константа */

#define WHITE 9 /* Пробельные элементы */

#define ER 20 /* Остальные (нежелательные) символы */ /* Содержание модуля CALC.C */ /*

* Этот файл содержит функцию main программы вычисления выражений. */

#include "CALC.H"

/* Объявить внешние функции */

extern exs_init(), ops_init(); /* STACK.С */ extern int getexpr(); /* SCAN.С */

extern int execute(); /* EXECUTE.С */

/**

Это основная управляющая функция программы вычисления

* выражений. **/

main () {

exs_init(); ops_init(); /* Инициализировать стеки */

getexpr(); ./* Считать выражение */

execute(); /* Выполнить вычисления */ }

main() {

extern exs_init(), ops_init(); /* STACK.С */

extern int getexpr(); /* SCAN.С */

extern int execute(); /* EXECUTE.С */

exs_init(); ops_init(); /* Инициализировать стеки */ getexpr(); /* Считать выражение */

execute(); /* Выполнить вычисления */ }

/* Содержание модуля EXECUTE.С */

/*

* Этот Файл содержит функцию execute программы вычисления выражений. */

#include "CALC.H"

extern int scanner(); /* SCAN.С */

extern float value; /* Число, извлеченное функцией

scanner */

extern int exs_push(); /* STACK.С */ extern float exs_pop(); /* STACK.С */

extern int ops_top{); /* STACK.С */ extern int ops_push(); /* STACK.С */ extern int ops_pop(); /* STACK.С */ /* Определить таблицу переходов */



/**

* Эта функция выполняет разбор и вычисление выражения. **/

execute() {

int token; /* Текущий элемент */

int action = 1; /* Из таблицы переходов */

token = scanner(); /* Извлечь Первый элемент */

while (action != DONE)

{

if (token = = NUMBER) /* Поместить число

в стек результатов */ {

exs__push(value) ; token = scanner(); } else {

/* Определить индекс в таблице переходов, используя текущий элемент и верхнюю операцию из стека операций */ action = trans_table[ops_top()][token]; #ifdef TRACE

printf("Действие = %d\n",action); #endif

switch (action) {

case 0: /* конец выражения */ /* изобразить результат */ printf("*f\n",exs_pop()); break; case 1:

ops_push(token); token = scanner(); break; case 2:

eval(); /* выполнить верхнюю операцию */ ops_push(token); token = scanner(); break; case 3:

ops_pop(); /* извлечь операцию из стека */ token = scanner(); break; case 4:

eval(); /* выполнить верхнюю операцию */ break; case 10:

printf("Ошибка в выражении\n");

action = 0;

break;

}

#ifdef TRACE

printf("Выполнение завершено\n");

#endif

return;

/* Содержание модуля SCAN.С */

/*

* Этот файл содержит функции scanner и getexpr программы вычисления

* выражений. */

#include "CALC.H"

#define MAX_LINE 81

static char line[MAX_LINE]; /* Содержит текущее выражение */

static int position; /* Индекс массива line */

float value; /* Число, извлеченное функцией scanner */ /*

* С помощью следующего массива каждому символу выражения присва-

* ивается определенный "класс", соответствующий типу элемента

* выражения, в состав которого входит этот символ. Каждому эле-

* менту приписан числовой код в диапазоне от 0 до 20 (20 озна-

* чает наличие в элементе недопустимого символа). Этот массив

* рассчитан на подмножество смежных символов от пробела до цифры

* 9. */

static char token_codes[26] =

{ WHITE,ER.ER,ER,ER.ER,ER,ER, /* Пробел,!,",#,$,%,&,'*/ LPAREN.RPAREN,MULT,PLUS, /* (,),*,+ */ ER,MINUS,DOT,DIVIDE, /* , ,-,.,/*/

NUMBER,NUMBER.NUMBER,NUMBER, /* 0,1,2,3 */ NUMBER,NUMBER,NUMBER,NUMBER, /* 4,5,6,7 */ NUMBER,NUMBER /* 8,9 */

}; /**

* Эта функция отвечает за поиск очередного элемента выражения.

* Элементами могут быть операции

* +,-,* или /

* или операнд (в данной версии мы ограничимся одной цифрой). **/

Int scanner () (

char ch; /* Текущий символ выражения */

int token; /* Текущий элемент выражения */

int index; /* Индекс массива token_codes */

float divisor; /* Масштабный коэффициент для дробной части числа */

ch = line[position];

if (ch = = '\0') return(END); /* Конец выражения */

if (ch < ' ':: ch > '9') return(ER); /* Только в этом диапазоне */

/* Использовать символ как индекс в массиве.

* Вначале масштабировать его так, чтобы пробелу

* отвечал нулевой индекс. */

index = ch - ' ';

token = token_codes[index];

switch (token)

{

case DOT:

token = NUMBER; сазе NUMBER: /* Разобрать число */

value = 0;

divisor - 1;

while (token_codes[ch - ' '] = = NUMBER) .

{

value = value*10 + (ch - '0');

ch = line[++position]; }

if (ch = = '.') {

ch = line(++position];

while (token_codes[ch - ' '] = = NUMBER)

{

divisor = divisor*10; value = value*10 + (ch - '0'); ch = line[++position]; } }

value = value/divisor; break; default:

position++; break; } #ifdef TRACE

printf("Элемент = %d\n".token); #endif

return(token);

}

/**

* Эта функция выдает пользователю приглашение к вводу выражения

* и считывает вводимое выражение. **/

getexpr()

{

int ch; /* Текущий введенный символ */

extern getline(); /* GETLINE.C */

printf(" * ");

getline(line,MAX_LINE);

position =0; /* Установить значение внешнего индекса на начало строки */

return; } /* Содержание модуля GETLINE.C */

/*

* Этот файл содержит функцию getline. */

#include "stdio.h" /* Может понадобиться функция getchar */ /**

* Эта Функция считывает строку данных с клавиатуры и запоминает

* ее в заданном массиве символов. Она возвращает число считанных

* символов. **/

getline(array, size)

char array[]; /* Массив символов соответствующей длины */

int size; /* Максимальная длина массива */

{

register int i; /* Позиция в массиве */

int с; /* Введенный символ */

i = 0;

/* Игнорировать ведущие пробельные символы */

while (((с = getchar()) = = '\n') ::

(с = = ' ') || ,' (с = = '\t'));

/* Считывать символы, пока не будет обнаружен возврат каретки */ while (с != '\n') {

/* Не переполнять массив -- оставить место для нулевого байта */

if (i < (size - 1)) {

array[i++] = c; } с = getchar();

array[i] = '\0'; /* Завершить строку нулевым байтом */

return(i); } /* Содержание модуля EVAL.C */

/*

* Этот файл содержит функцию eval программы вычисления

* выражений. */

#Include "CALC.H"

extern float ехs_рор(); /* STACK.С */

extern int орs_рор(), ехs_рush(); /* STACK.С */ /**

* Эта Функция выполняет заданную операцию над двумя числами,

* которые снимаются с верха стека операндов. **/

eval () {

int operator;

float left, right, result;

/* Извлечь операцию и операнды из стеков */

operator = орs_рор();

right = exs_pop(); left = ехs_рор();

/* Выполнить операцию и поместить результат обратно в стек */

switch (operator)

{

case PLUS:

result = left + right; exs_push(result); break; case MINUS:

result = left - right; exs_push(result); break; case MULT:

result = left*right; exs_push(result); break; сазе DIVIDE:

if (right != 0.0) /* Проверить делитель на нуль */ {

result = left/right; exs_push(result);

} else

{

printf("Ошибка: деление на нуль\n"); }

break; }

#ifdef TRACE

printf("eval: op = %d, left = %f, right = %f, result = %f\n" ,

operator,left,right,result); #endif

return; }

/* Содержание модуля STACK.С */

/*

Этот файл содержит функции для работы со стеком программы

* вычисления выражений.

*/

#include "CALC.H"

/**

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

* В состав этой группы входят следующие функции:

Стек результатов Стек операций Назначение

exs_init ops_init инициализация стека

exs_push ops_push поместить в стек

ехs_рор орs_рор извлечь из стека

exs_top ops_top посмотреть содержимое

* вершины стека **/

/* Определить стеки и индексы стеков */

«define MAX_STACK 20 /* Максимальный размер стека */

/*

* Стеки содержат на один элемент больше максимального размера

* для удобства проверки пустоты и переполнения. */

static float ex_stack[MAX_STACK+l]; /* Стек результатов (содержит

числа) */ static int op_stack[MAX_STACK+l]; /* Стек операций (содержит

символы) */

static int ex_index, op_index; /**

* Инициализировать стек результатов и указатель стека (индекс). **/

exs_init()

{

ex_index = 0;

ex_stack[0] = EMPTY; /* Присвоить значение EMPTY ("пуст") */

return; } /**

* Поместить "значение" в стек результатов. Если все нормально,

* возвратить SUCCESS, если стек полон, возвратить FAILURE. **/

int exs_push(value)

float value;

{

int result = SUCCESS;

if (ex_index < MAX_STACK)

{

ex_stack[++ex_index] = value;

}

else

{

result = FAILURE;

} return(result);

} /**

* Извлечь значение, помешенное в стек последним. Возврат

* это значение. Если стек пуст, то возвратить EMPTY. **/

float exs_pop()

{

float result;

result = ex_stack[ex_index]; if (ex_index > 0) ex_index--; return(result);

}

I /**

Возвратить содержимое вершины стека (не изменяя значение

* указателя стека). **/

I float exs_top()

return(ex_stack[ex_index]);

/**

* Инициализировать стек операций и указатель стека (индекс). **/

ops_init()

{

op_index = 0;

op_stack[0] = EMPTY; /* Присвоить EMPTY ("пуст") */

return;

}

/**

* Поместить "операцию" в стек операций. Если все нормально,

* возвратить SUCCESS, если стек полон, возвратить EMPTY. **/

int ops_push(operator) int operator;

{

int result = SUCCESS;

if (op_index < MAX_STACK)

{

op_stack[++op_index] = operator;

} else

{

result = FAILURE;

}

return(result); } I /**

* Извлечь значение, помещенное в стек последним. Возвратить

* это значение. Если стек пуст, то возвратить EMPTY. **/

int ops_pop( )

int result;

result = op_stack[op_index]; if (op_index > 0) op_index--; return(result);

/**

* Возвратить содержимое вершины стека (не изменяя значение

* указателя стека). **/

int ops_top( )

return( op_stack[op_index ]) ;

}

ЛИТЕРАТУРА


Kernighan, B.W., and D.M.Ritchie, The С Programming Language. Erigieewood Cliffs, N.J.: Prentice-Hall, i 1978, p. 41.

Ritchie, D.M., S.C.Johnson, M.E.Lesk, and B.W.Kernighan, "The С Programming Language", The Bell System Technical Journal, July-August 1978, Vol. 57, No. 6, Part 2, pp. 1991-2019.



Похожие:

Выражения и операции в языке си iconУроки №3-4 тема: " Программирование линейных алгоритмов. Стандартные математические функции Паскаля. Модуль crt". Основные операции в Паскале
В тп 0 все операции делятся на: математические, логические, операции с символами и строкам, операции над множествами, операции отношения,...
Выражения и операции в языке си icon1. Алгоритм это
Выберите верное представление арифметического выражения  на алгоритмическом языке
Выражения и операции в языке си iconПостроение таблиц истинности логических выражений Приоритет логических операций
При вычислении значения логического выражения (формулы) логические операции вычисляются в определенном порядке, согласно их приоритету:...
Выражения и операции в языке си iconПравила выполнения определенных действий; ориентированный граф, указывающий порядок выполнения некоторого набора команд
Выберите верное представление арифметического выражения  на алгоритмическом языке
Выражения и операции в языке си iconФорма заявки
Заполните данную форму на русском языке и отправьте в Центр Защиты Прав сми по адресу gala@media vrn ru с пометкой "Защита свободы...
Выражения и операции в языке си iconТема : Оператор присваивания в языке программирования
«вычислить значения выражения справа от знака присваивания := и записать результат в переменную a»; при этом значения других переменных...
Выражения и операции в языке си iconТема : Оператор присваивания в языке программирования
«вычислить значения выражения справа от знака присваивания := и записать результат в переменную a»; при этом значения других переменных...
Выражения и операции в языке си iconТема : Оператор присваивания в языке программирования
«вычислить значения выражения справа от знака присваивания := и записать результат в переменную a»; при этом значения других переменных...
Выражения и операции в языке си iconФорма заявки на участие в тренингововом курсе
Заполните данную форму на русском языке и отправьте в Центр Защиты Прав сми по адресу gala@media vrn ru с пометкой "Защита свободы...
Выражения и операции в языке си iconЛокативность как периферийное средство выражения темпоральных отношений в современном немецком языке
Защита состоится 30 октября 2007 г в 13. 00 на заседании диссертационного совета k-212. 216. 04 при Самарском государственном педагогическом...
Выражения и операции в языке си iconОперации над нечеткими числами
Целый раздел теории нечетких множеств – мягкие вычисления (нечеткая арифметика) вводит набор операций над нечеткими числами. Эти...
Разместите кнопку на своём сайте:
Документы


База данных защищена авторским правом ©podelise.ru 2000-2014
При копировании материала обязательно указание активной ссылки открытой для индексации.
обратиться к администрации
Документы

Разработка сайта — Веб студия Адаманов