Помощь - Поиск - Пользователи - Календарь
Полная версия: Ошибка сегментирования
Форум «Всё о Паскале» > Современный Паскаль и другие языки > Ада и другие языки
Игорь
Хочу написать програмку-калькулятор (по книге Кернигана и Ритча).
Суть простая: вводится выражение в форме обратной польской нотации, оно вычисляется и выводится результат.

Программу разбил на функции:

1) GetLine - получение строки (работает нормально).
2) calculate - вычисление и получение результата, вот тут проблема, при входе в функцию появляется та самая ошибка сегментирования.
3) getop - получение следующего элемента из строки. (тоже самое).
4) pop и push - работа со стеком. (работают тоже не правильно, но по крайней мере из-за них программа не вылетает).

Компилятор GCC.

calculate (Показать/Скрыть)

getop (Показать/Скрыть)
IUnknown
Ты бы полностью программу показал... Если у тебя до printf даже не добирается - то зачем все, что ПОСЛЕ, если нет того, что ДО?
Игорь
Вообще программа была разбита файлы, но я соединил всё в один.

Calculator (Показать/Скрыть)


Проверил в gdb (представления не имею как им пользоваться, просто где-то видел как его запускать) он указывает на функцию getop, а точнее на цикл for.
IUnknown
На предупреждения компилятора внимание не хочешь обратить?
\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 ) /* у тебя был обычный слэш, нужен обратный */

И это, покажи, какую ты строку вводишь для теста...
IUnknown
Еще 2 ошибки:

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 не передавая как аргумент и не делая глобальной переменной?
IUnknown
Цитата
// когда я проверил stack[0] он оказался равен 0 вместо 1.5
Не подтверждается. Установил брейкпойнт в точке

   else if ( expr[i] == '+' || expr[i] == '-' || expr[i] == '*' || expr[i] == '/' )
{
i += 2; // <--- Вот тут.
return expr[i];
}

, дальше у нас с gdb состоялся вот такой диалог:

(gdb) print i
$1 = 8
(gdb) print stack
$2 = {1.5, 2, 0 <repeats 18 times>}

, то есть, заносятся значения в стек правильно. Но вот теперь еще одна проблема: у тебя 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[ ])?
IUnknown
Цитата
хотелось бы иметь возможность передавать параметры через ком. строку. Для этого нужно сделать так: main (char param[ ])?
Для этого с момента создания языка С достаточно сделать так:

int main(int argc; char *argv[])
{
/* ... */
}
, но учти: все то, что разделено пробелом - это разные параметры, если только ты их не обернешь кавычками... То есть,
mycalc 2 1.5 +
посчитает, что были переданы 4 параметра (от 0 до 3), а вот
mycalc "2 1.5 +"
всего 2 (нулевой и первый)...

Цитата
ведь калькулятора у меня на linux нету
У тебя очень странный Linux, хотелось бы заметить smile.gif Обычно калькулятор есть...
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.