Язык Си в примерах/Калькулятор выражений в обратной польской нотации: различия между версиями
Содержимое удалено Содержимое добавлено
Karagota (обсуждение | вклад) |
Greck (обсуждение | вклад) Нет описания правки |
||
Строка 28:
НИже приведена постая реализация такого калькулятора на C. Заметим, что в ней нет никаких проверок ошибок ввода (если первый символ есть знак операции, она "умрёт"), а также стэк с его операциями pop и push не выделен в явную структуру.
Роль стэка играет обычный массив (stack[
Число (<tt>sp</tt>-1) равно индексу ячейки, являющейся вершиной стека.
#include <stdio.h>
int main()
{
int stack[
int sp = 0; // индекс ячейки, куда будет push-иться очередное число
// (sp-1) = индекс ячейки, являющейся вершиной стека
int sp = 0;▼
while(!feof(stdin)) {
switch(c)
case '\0':▼
break;
case '=':
printf("Result = %d\n", stack[
break;
case '+':
stack[sp-2] = stack[sp-2] + stack[sp-1]; sp--;
▲ sp--;
break;
case '-':
stack[sp-2] = stack[sp-2] - stack[sp-1]; sp--;
sp--;▼
break;
case '*':
stack[sp-2] = stack[sp-1] * stack[sp-2]; sp--;
sp--;▼
break;
case '/':
stack[sp-2] = stack[sp-1] / stack[sp-2]; sp--;
sp--;▼
break;
default:
if(scanf("%d", &x) != 1) {
fprintf(stderr, "Can't read integer\n");
stack[sp] = x; sp++;
}
}
Строка 74:
}
===Пример работы программы===
> ./stack
1 2 3 4 + * + =
Result = 15
1 2 + 3 4 + * =
Result = 21
===Задания===
# Введите входные следующие входные данные <tt>1 2 3 * * * * = = = =</tt>. К чему это привело? Добавьте к приведенной программе
"защиту от дурака".
# Введите входные данные состоящие из 10000 единиц и 9999 знаков +. Сможет ли программа отработать на этих входных данных?
===Замечания===
# У данной программы есть целый ряд недостатков
:* Программа не будет работать, если количество элементов в стеке превысит 1000
:* Программа не замечает некорректные входы и отрабатывает их с непредсказуемыми последстиями.
:* В программе явно не отображено, что присутствует структура данных "стек", что затрудняет сторонним разработчикам осуществлять доработку данной программы.
=== Реализация калькулятора с явным определением операций со стеком ===
Введем операции работы со стеком в программу. Это повысит читаемость кода и облегчит понимание заложенной в программу логики.
#include <stdio.h>
#include <malloc.h>
fprintf(stderr, "Невозможно выполнить POP для пустого стека.\n");
m_pt = 0;▼
▲ int pop(void) {
void push(int a) {▼
int empty() {▼
return (m_pt == 0);▼
}
};
};
}
int main() {
int i;
while(!feof(stdin)) {
Строка 121 ⟶ 129 :
int x;
switch (c) {
case '\n':
case ' ' : break;
case '=' : printf("Result = %d\n", s.pop()); break;
case 27 : goto RESULT;
case '+' :
case '-' :
case '*' :
default:
ungetc(c, stdin);
Строка 142 ⟶ 149 :
RESULT:
i = 0;
while(!
printf("Stack[%d] = %d\n", i, s.pop());
i++;
Строка 148 ⟶ 155 :
return 0;
}
В данной программе в некоторой степени реализована "защита от дурака", а именно, если вводится выражение, в котором число операций превосходит число
помещенных в стек элементов (например <tt>1 2 + *</tt>), то программа не допустить уменьшиния переменной <tt>sp</tt> до отрицательных значений, а выдаст предупреждение <tt>"Невозможно выполнить POP для пустого стека.</tt>.
Кроме защиты от дурака-пользователя необходима еще защита от дурака-программиста, который возьмет ваш код, решит его использовать и дорабатывать.
Точнее, нужно просто соблюдать некоторые правила, которые не позволили бы программисту, который включил ваш код в свой проект получить ошибки,
связанные с пересечением имён, испольуемых им в своих файлах и вами, в фале <tt>stack.c</tt>.
В первую очередь, не рекомендуется объявлять глобальные переменные, которые не являются всеобщим достоянием, а относятся к вашим личным внутренним
делам.
|