1 Востаннє редагувалося Ярослав (23.02.2013 13:16:56)

Тема: Керніган і Рітчі, вправа 5.10, робота з аргументами командного рядка

Перед тим, як я викладу код, я хочу сказати всім велике дякую! Я застряг на покажчиках і ніяк не міг виконати цю вправу, проте завдяки вашим підказкам і поясненням таки її написав.
Завдання полягає в тому, що агрументами командного рядку задається зворотній польський запис, наприклад 2 2 + 3 -5 * / =, який має обчислюватись так (2+2) / (3*(-5)) =.
Для вправи я використав стек так, як в книзі було показано раніше. В коді новачкам буде корисно подивитись на оператори, цикли, адресну арифметику, роботу із багатовимірними масивами і покажчики на масиви покажчиків. Досвідчені програмісти нічого корисного для себе тут, скоріш за все, не знайдуть.

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define MAX 1024

float stack[MAX];
float *ptr_s = stack;
void push_s(float);
float pop_s(void);

int main(int argc, char *argv[])
{
    float op;
    *argv++;
    while(--argc > 0){
        if( isdigit(**argv) || **argv == '.' || (**argv == '-' && (isdigit(*(*argv+1)) || *(*argv+1) == '.')) )
            push_s(atof(*argv));
        else
            switch(**argv){
                case '+':
                    printf("+\n");
                    push_s(pop_s() + pop_s());
                    break;
                case '-':
                    printf("-\n");
                    op = pop_s();
                    push_s(pop_s() - op);
                    break;
                case '*':
                    printf("*\n");
                    push_s(pop_s() * pop_s());
                    break;
                case '/':
                    printf("//\n");
                    if((op = pop_s()) != 0)
                        push_s(pop_s() / op);
                    else
                        printf("Null divisor\n");
                    break;
                case '=':
                    printf("%.4f\n", pop_s());
                    break;
                default:
                    printf("Unknown op: %s\n", *argv);
                    break;
            }
        argv++;
    }
    return 0;
}

void push_s(float s){
    printf("p: %.1f\n", s);
    if(ptr_s+1 > stack+MAX)
        printf("Stack overflow\n");
    else
        *ptr_s++ = s;
}

float pop_s(void){
    if(ptr_s-1 < stack){
        printf("Stack underflow\n");
        return 0;
    }
    else
        return *--ptr_s;
}

2

Re: Керніган і Рітчі, вправа 5.10, робота з аргументами командного рядка

Прихований текст

http://s2.postimage.org/oz8bmgpi1/image.png

Прихований текст

http://s18.postimage.org/vl76x0ox5/image.png

Прихований текст

http://s11.postimage.org/c802xxtqr/image.png

3

Re: Керніган і Рітчі, вправа 5.10, робота з аргументами командного рядка

На моїй машині порахувалось, я іще один раз скопіював код, спробуйте зараз.

4

Re: Керніган і Рітчі, вправа 5.10, робота з аргументами командного рядка

Результат

http://s11.postimage.org/t6e9b4m37/image.png

Падає на операції множення.

З.І: які у вас компілятор, середовище?

5

Re: Керніган і Рітчі, вправа 5.10, робота з аргументами командного рядка

void push_s(float s){
    if(ptr_s+1 > stack+MAX)
        printf("Stack overflow\n");
    else
        *ptr_s++ = s;
    printf("Entered: %f\n", s);
}

Сюди навіть не попадаємо:

case '*':
{
    float x1 = pop_s(), x2=pop_s();
    printf("%f*%f\n", x1, x2);
    push_s(x1*x2);
}
   break;

6 Востаннє редагувалося Ярослав (23.02.2013 13:17:58)

Re: Керніган і Рітчі, вправа 5.10, робота з аргументами командного рядка

http://replace.org.ua/misc.php?action=pun_attachment&amp;item=104&amp;download=0

Unknown op: a
p: 2.0
p: 2.0
+
p: 4.0
4.0000
p: 2.0
p: 2.0
-
p: 0.0
0.0000
p: 2.0
p: 2.0
*
p: 4.0
4.0000
p: 2.0
p: 2.0
//
p: 1.0
1.0000
p: 2.2
p: 2.2
+
p: 4.4
p: 3.3
//
p: 1.3
p: -4.4
*
p: -5.9
p: 1023.1
+
p: 1017.2
1017.2333
Press any key to continue...

Я змінив місце інкрементування, можливо воно було небезпечним у понятті кросплатформеності, спробуйте зараз (перше повідомлення).

Post's attachments

Командний рядок.JPG 43.93 kb, 354 downloads since 2013-02-23 

7

Re: Керніган і Рітчі, вправа 5.10, робота з аргументами командного рядка

Судячи з усього, є вихід за межі масиву, коли зі стеку витягаються елементи, бо у мене, крім нормального виводу, видало список файлів на диску і якусь нісенітницю.

8

Re: Керніган і Рітчі, вправа 5.10, робота з аргументами командного рядка

Що можна із цим зробити?

9

Re: Керніган і Рітчі, вправа 5.10, робота з аргументами командного рядка

Вранці подивлюся ще раз. Код загалом робочий, але ж має бути причина, чому його запуск без будь-яких виправлень видав мені неочікувані результати. :)

10

Re: Керніган і Рітчі, вправа 5.10, робота з аргументами командного рядка

Добрий ранок на Форумі. :)

Недарма глючило на зірочці. Зверніть увагу на значення argc:

Прихований текст

http://s13.postimage.org/qkf72lkw7/image.png

Більше того, коли я запустив програму в MSYS - додалося проблем із діленням (і знову увага на argc):

Прихований текст

http://s2.postimage.org/4cq6izspl/image.png

А сіль ось, думаю, у чому. Ви запускали програму, задавши параметри безпосередньо у своєму IDE. Я ж запускав екзешник у консолі, яка інтерпретувала ці символи як службові. І якщо зірочка де-факто містила граблі, то для "/" це виявилося лише "під недолінуксом", себто у MSYS, де, вочевидь, це зрозумілося як коренева директорія.

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

Вашу програму все ж удалося запустити завдяки наступній кислоті:

Прихований текст

http://s2.postimage.org/u0vbyj9pl/image.png

Однак це небезпечно, оскільки зірочка все одно розпізнається як службовий символ-маска для файлів, а програма виконалася лише тому, що у каталозі не було файлів, імена яких закінчувалися на два апострофи. :)

Висновок з того наступний. Глюк зірочки (і слеша для *nix) - не ваша вина, але особливість роботи консолі. Та якщо ви створюєте програму, ліпше, якщо вона буде надійною за різних обставин, адже замовник здебільшого мало цікавитиметься, чому воно не працює, зате вимагатиме, щоб воно працювало у нього.

З.І: щодо того, як виправити цю особливість:
У вашій програмі перевіряється лише перший символ параметра. Тому для неї оператори "+", "++" і навіть "+№;№;;;" абсолютно ідентичні. Вихід - strcmp().
А от проблема зірочки веселіша. Допоможе, мабуть, лише зміна логіки програми, себто запуск вигляду

$ myprog.exe '2 2 + 3 -5 * /'

де рядок ПОЛІЗу передається одним параметром. Така логіка, до слова, присутня у більшості інтерпретаторів. :)