1. Пользуйтесь тегами кода. - [code] ... [/code] 2. Точно указывайте язык, название и версию компилятора (интерпретатора). 3. Название темы должно быть информативным. В описании темы указываем язык!!!
Хочу написать програмку-калькулятор (по книге Кернигана и Ритча). Суть простая: вводится выражение в форме обратной польской нотации, оно вычисляется и выводится результат.
Программу разбил на функции:
1) GetLine - получение строки (работает нормально). 2) calculate - вычисление и получение результата, вот тут проблема, при входе в функцию появляется та самая ошибка сегментирования. 3) getop - получение следующего элемента из строки. (тоже самое). 4) pop и push - работа со стеком. (работают тоже не правильно, но по крайней мере из-за них программа не вылетает).
Компилятор GCC.
calculate(Показать/Скрыть)
double calculate (char expr[]) {
char type; double op2;
printf ("Начало вычисления"); // я это специально здесь поставил, но до этого места даже не доходит.
while ( ( type = getop(expr) ) != EOE ) switch ( type ) { case NUMBER: ; break; case '+': push (pop() + pop()); break; case '-': op2 = pop(); push (pop() - op2); break; case '*': push (pop() * pop()); break; case '/': if ( (op2 = pop()) != 0.0) push (pop() / op2); else printf ("Ошибка: деление на нуль.\n"); break; default: printf ("Ошибка: неизвестная операция.\n"); break; }
return pop();
}
getop(Показать/Скрыть)
int getop (char expr[]) {
static int i; /* getop будет возвращать найденную операцию или NUMBER если найдено число, потом после того как calculate выполнит необходимые действия работа будет продолжена там, где остановилась.*/ int j; char op[10];
На предупреждения компилятора внимание не хочешь обратить?
\home\volvo\Devel\c_tst\main.c|14|warning: return type defaults to 'int'| \home\volvo\Devel\c_tst\main.c||In function 'getop':| \home\volvo\Devel\c_tst\main.c|86|warning: suggest parentheses around assignment used as truth value| \home\volvo\Devel\c_tst\main.c|93|warning: multi-character character constant| \home\volvo\Devel\c_tst\main.c|95|warning: implicit declaration of function 'atof'| \home\volvo\Devel\c_tst\main.c||In function 'pop':| \home\volvo\Devel\c_tst\main.c|42|warning: control reaches end of non-void function| ||=== Build finished: 0 errors, 5 warnings (0 minutes, 0 seconds) ===|
В 86 строке однозначная ошибка:
else if ( expr[i] == '.' && j > 0 ) /* у тебя был один знак "=" */
, то же самое касается и 93 строки:
else if ( ( expr[i] == ' ' || expr[i] == '\t' ) && i > 0 ) /* у тебя был обычный слэш, нужен обратный */
И это, покажи, какую ты строку вводишь для теста...
1) перед тем, как заталкивать число в стек, надо отсечь весь мусор в строке. Это очень просто:
else if ( ( expr[i] == ' ' || expr[i] == '\t' ) && i > 0 ) { op[j] = '\0'; /* Вот и все, теперь atof будет воспринимать только число, до позиции j */ push (atof(op)); return NUMBER; }
2) более серьезный просчет:
static int i; /* Я так понимаю, это чтобы i сохранялось между вызовами? */ int j; char op[10]; putchar ('.'); for (i = 0, j = 0; expr[i] != '\0' || expr[i] != '\n' || expr[i] != '='; i++) /* <--- Вот сюда теперь посмотри */
Именно для этого ты описал i статической? Чтобы при втором заходе в getop() все, что уже было прочитано из строки, пропускалось, и читалось со следующего символа? Не получится, ты в начале цикла переменную i обнуляешь, то есть при самом удачном раскладе ты получишь зацикливание, будешь все время читать только первый токен из строки (о чем тебе и говорит gdb). Применяй какой-нибудь другой способ для подобных трюков...
Спасибо за найденные ошибки, с помощью gdb нашёл ещё несколько в логике getop, поэтому немножко её изменил. Однако, проблемы ещё остались.
Строка для теста: 1.5 2 +
else if ( ( expr[i] == ' ' || expr[i] == '\t' ) && j > 0 ) { op[j] = '\0'; push (atof(op)); // когда я проверил stack[0] он оказался равен 0 вместо 1.5 j = 0; }
for (j = 0; expr[i] != '\0' || expr[i] != '\n' || expr[i] != '='; i++) /* здесь при втором вхождении в функцию, expr[i] = '\0', но цикл всё равно идёт дальше. */
Цитата
Применяй какой-нибудь другой способ для подобных трюков...
Т.е. если я захочу сохранить значение переменной после выхода из функции я как-то ещё поступить? Как? И можно ли использовать, например, expr в getop не передавая как аргумент и не делая глобальной переменной?
, то есть, заносятся значения в стек правильно. Но вот теперь еще одна проблема: у тебя i вылетает за пределы строки, и возвращаться из функции будет уже не "+", а мусор, у меня это был пробел, потому что завершающий '\0' находится в 7-ом символе (начиная от 0)... Может, ты еще что-то в коде поменял, я не знаю, все, что ты написал - я изменил...
Я, в принципе, могу тебе и так сказать, где ты ошибся: При возвращении знака операции надо сначала запомнить тот символ, который лежит в expr[ i ], потом увеличить i, и только потом возвращать символ. Запомненный, естественно...
else if ( expr[ i ] == '+' || expr[ i ] == '-' || expr[ i ] == '*' || expr[ i ] == '/' ) { char ch = expr[ i ]; i += 2; return ch; }
, хотя я бы еще вместо того, чтоб все время тягать такие длинные проверки (символ сравнивать с несколькими константами), делал бы так:
else if (strchr("+-*/", expr[ i ])) { ... }
Цитата
Т.е. если я захочу сохранить значение переменной после выхода из функции я как-то ещё поступить? Как?
Для начала эту переменную надо инициализировать (static int i = 0;). Потому что инициализация статической переменной производится на этапе компиляции, и больше - никогда (при входе в функцию переменная обнуляться не будет).
atof стал считать, когда я подключил stdlib, а в for должно было стоять не || , а &&.
Кстати, моя програмка может мне даже и пригодиться, ведь калькулятора у меня на linux нету, но хотелось бы иметь возможность передавать параметры через ком. строку. Для этого нужно сделать так: main (char param[ ])?
хотелось бы иметь возможность передавать параметры через ком. строку. Для этого нужно сделать так: main (char param[ ])?
Для этого с момента создания языка С достаточно сделать так:
int main(int argc; char *argv[]) { /* ... */ }
, но учти: все то, что разделено пробелом - это разные параметры, если только ты их не обернешь кавычками... То есть, mycalc 2 1.5 + посчитает, что были переданы 4 параметра (от 0 до 3), а вот mycalc "2 1.5 +" всего 2 (нулевой и первый)...
Цитата
ведь калькулятора у меня на linux нету
У тебя очень странный Linux, хотелось бы заметить Обычно калькулятор есть...