1 Востаннє редагувалося Ярослав (20.12.2012 03:12:51)

Тема: Ознака числа і неодноразовий виклик функції getchar() Сі

Вітаю!
Намагаюсь розібратись із даною вправою із книги K&R

#include <stdio.h>
#include <stdlib.h> /* atof() */

#define MAXOP 100 /* максимальний розмір оператора або операнда */
#define NUMBER '0' /* ознака числа */

int getop(char []);
void push(double);
double pop(void);

/* PL calc*/
int main(int argc, char *argv[])
{
    int type;
    double op2;
    char s[MAXOP];

    while((type = getop(s)) != EOF){
        switch(type){
        case NUMBER:
            push(atof(s));
            break;
        case '+':
            push(pop() + pop());
            break;
        case '*':
            push(pop() * pop());
            break;
        case '-':
            op2 = pop();
            push(pop() - op2);
            break;
        case '/':
            op2 = pop();
            if(op2 != 0.0)
                push(pop()/op2);
            else
                printf("Error: Divide by zero");
            break;
        case '\n':
            printf("\t%.8g\n", pop());
            break;
        default:
            printf("Error: Undefined operation %s\n", s);
            break;
        }
    }
    return 0;
}

#define MAXVAL 100
int sp = 0;
double val[MAXVAL]; /* стек */

/* push: додає значення в стек */
void push(double f){
    if(sp < MAXVAL)
        val[sp++] = f;
    else{
        printf("Stack overflow\n");
    }
}

/* pop: повернути останній доданий до стеку елемент в якості результату */
double pop(void){
    if (sp>0)
        return val[--sp];
    else{
        printf("Stack underflow\n");
        return 0.0;
    }
}

#include <ctype.h> /* isdigit() */

int getch(void);
void ungetch(int);

int getop(char s[]){
    int i, c;
    
    while ((s[0] = c = getch()) == ' ' || c == '\t')
        ;
    s[1] = '\0';
    if(!isdigit(c) && c != '.')
        return c; /* не число */
    i = 0;
    if(isdigit(c)) /* накопичуємо цілу частину */
        while(isdigit(s[++i] = c = getch()))
            ;
    if(c=='.') /* накопичуємо дробову частину */
        while(isdigit(s[++i] = c = getch()))
            ;
    s[i] = '\0';
    if(c != EOF)
        ungetch(c);
    return NUMBER;
}

#define BUFSIZE 100

char buf[BUFSIZE]; /* буфер для ungetch */
int bufp = 0; /* наступна вільна позиція в буфері */

int getch(void)
{
    return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c) /* повернути символ на ввід*/
{
    if(bufp >= BUFSIZE)
        printf("ungetch: too much symbols\n");
    else
        buf[bufp++] = c;
}

В принципі ясно все окрім цих рядків:

#define NUMBER '0' /* ознака числа */
[...]
        case NUMBER:
            push(atof(s));
            break;

Ми створюємо константу NUMBER зі значенням 48
Яким чином case визначає що ввід - це цифра? Адже
case NUMBER = case '0' = case 48;
?

2

Re: Ознака числа і неодноразовий виклик функції getchar() Сі

Якщо s є числом, то функція getop() повертає контанту NUMBER.

3 Востаннє редагувалося Ярослав (20.12.2012 03:38:14)

Re: Ознака числа і неодноразовий виклик функції getchar() Сі

З'явилося іще одне питання

int getop(char s[]){
int i, c;
while ((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
if(!isdigit(c) && c != '.')
return c; /* не число */
i = 0;
if(isdigit(c)) /* накопичуємо цілу частину */
while(isdigit(s[++i] = c = getch()))
;
if(c=='.') /* накопичуємо дробову частину */
while(isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
if(c != EOF)
ungetch(c);
return NUMBER;
}
int getch(void)
{
return (bufp > 0) ? buf[--bufp] : getchar();
}

Тут ми звертаємось до функції getch() тричі, але користувач вводить дані лише один раз.
Яким чином відбувається взаємодія між getop() і getch()
Керніган у своєму описі функції каже, що ми "посилаємо символ назад у ввід". Тобто треба розуміти це так, що те що повертається getch, тобто певна послідовність символів вводу опрацьовується як масив і поки повністю не буде опрацьовано то функцію викликати іще раз не можна?
Поясніть будь ласка, бо важко зрозуміти.

4

Re: Ознака числа і неодноразовий виклик функції getchar() Сі

Там в коментах все написано. getch() - читає один символ.

5 Востаннє редагувалося Ярослав (20.12.2012 03:42:02)

Re: Ознака числа і неодноразовий виклик функції getchar() Сі

Я розумію це, мені цікаво чому рядки

if(isdigit(c)) /* накопичуємо цілу частину */
while(isdigit(s[++i] = c = getch()))
;
if(c=='.') /* накопичуємо дробову частину */
while(isdigit(s[++i] = c = getch()))
;

не викликають функцію знову, а працюють із даними які уже містяться в пам'яті.

6

Re: Ознака числа і неодноразовий виклик функції getchar() Сі

якраз викликають getch() і записують в s[++i]

7 Востаннє редагувалося Ярослав (20.12.2012 04:04:50)

Re: Ознака числа і неодноразовий виклик функції getchar() Сі

while ((s[0] = c = getch()) == ' ' || c == '\t')
while(isdigit(s[++i] = c = getch()))
while(isdigit(s[++i] = c = getch()))

3-чі викликають getch()
І всі три виклики відбуваються тоді коли змінна bufp=0.
Всі три рази має cпрацювати функція getchar вкладена в getch, але дані пропонують ввести лише 1 раз. Чому?

8 Востаннє редагувалося Replace (20.12.2012 04:12:17)

Re: Ознака числа і неодноразовий виклик функції getchar() Сі

getchar() - просто читає один символ з потоку введення, а не одразу весь рядок.
Шматок више 3-й рядок просто не спрацює ніколи :)

9

Re: Ознака числа і неодноразовий виклик функції getchar() Сі

Я довго думав, але все іще до кінця не зрозумів.
Виходить ми викликаємо певну функцію, та в свою чергу викликає getline, а по виклику getline відбувається запис або читання в або із буфера введення (cin чи stdin).
Можна якийсь матеріал про буфер по темі?

10 Востаннє редагувалося Patron (20.12.2012 18:27:22)

Re: Ознака числа і неодноразовий виклик функції getchar() Сі

2keithfay не розумію вашого запитання. Можете чітко сказати чого саме ви не розумієте?

while ((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0'; // цього я не зрозумів
if(!isdigit(c) && c != '.')
return c; /* не число */
i = 0;
if(isdigit(c)) /* накопичуємо цілу частину */
while(isdigit(s[++i] = c = getch()))
;
if(c=='.') /* накопичуємо дробову частину */
while(isdigit(s[++i] = c = getch()));

Перший цикл служить для того щоб "промотати" пробіли і табуляції, потім якщо перший символ після можливих пробілів не цифра - виходимо повертаючи цей символ, якщо цифра то читаємо символи першої частини числа поки виконуєься isdigit, якщо ми стали на крапку після першої частини числа то потрібно прчитати частину числа після крапки і це робить останній цикл.Число в результаті опиниться в масиві s. Коли число прочитане то ми повертаємо константу NUMBER як ознаку того що ми ввели в програму число.

11 Востаннє редагувалося Ярослав (22.12.2012 14:22:12)

Re: Ознака числа і неодноразовий виклик функції getchar() Сі

Дякую за пояснення. Я розумію дію програми. Мене цікавить що відбувається "всередині".
Конкретно мене цікавить як працює функція getch() і взаємодія функції getop()
Якби я тричі викликав функцію printf("123\n");, то отримав би вивід

123
123
123

А тут я тричі викликаю функцію getch()

return (bufp > 0) ? buf[--bufp] : getchar();

, яка тричі перевіряє чи bufp більше нуля і тричі отримує значення false, тричі має спрацювати функція getchar(). Але якщо запустити програму - то ви побачите, що ввести дані користувачу пропонують всього 1 раз. Replace мені пояснив - що це відбувається тому, що ми працюємо із буфером сin чи stdin, і що фразу накшталт "повернути символ на ввід" варто розуміти так, що ми працюємо із певною областю пам'яті, виділеною під буфер.
Ось цього моменту я не розумію і хочу розібратись.
Головним чином моє питання звучить так:

Чому 3-чі викликана функція getch()

тут    while ((s[0] = c = getch()) == ' ' || c == '\t')
тут    while(isdigit(s[++i] = c = getch()))
і тут    while(isdigit(s[++i] = c = getch()))

3-чі перевіряє чи bufp>0, усі 3 рази отримає значення false 0 і всі три рази запускає функцію getchar(), але программа пропонує ввести дані лише 1 раз.
тобто код

while((c = getchar) != '\n')
printf("1");
while((c = getchar) != '\n')
printf("2");
while((c = getchar) != '\n')
printf("3");

Запропонує 3-чі зробити ввід, а код

    while ((s[0] = c = getch()) == ' ' || c == '\t')
    while(isdigit(s[++i] = c = getch()))
    while(isdigit(s[++i] = c = getch()))

Пропонує зробити ввід лише 1 раз.

12

Re: Ознака числа і неодноразовий виклик функції getchar() Сі

Ось моє питання сформульоване трохи інакше і лаконічніше:
http://stackoverflow.com/questions/1406 … cin-buffer

13

Re: Ознака числа і неодноразовий виклик функції getchar() Сі

А відповідь дуже проста.
Є 3 потоки (тут дуже добре описується http://uk.wikipedia.org/wiki/Stdin#.D0. … 1.96.D0.B4)
Один раз ввівши дані і натиснувши Enter термінал Windows передасть по потоку 0 дані в пам'ять.
Для того щоб отримати дані із пам'яті, то треба теж передавати команди по потоку 0, так команда getchar() буде давати доступ до одного символу за раз.
Рядок, що міститься в пам'яті, думаю, можна порівняти із масивом типу char.
А коли ввід (та область пам'яті, в якій міститься рядок) буде пустий, то при використанні функції getchar() нам запропонують ввести дані для опрацювання.
Десь так, якщо щось не правильно написав - виправте мене.