Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > getchar() - вопрос с буфером ввода


Автор: russians 21.8.2012, 02:01
В книге K&R в первых главах даётся описание функций getchar и putchar, но пропущена проблема буфера.
Например, если выполнить код из этой статьи http://younglinux.info/c/getchar

Код

  char a, b, c;
 
  a = getchar();
  putchar(a);
  b = getchar();
  putchar(b);
  c = getchar();
  putchar(c);
 
  printf("\n");


то он не сработает, как ожидается, т.к. второй символ будет занят \n, а третий вторым символом. Плюс - непонятно из статьи, почему на выводе мы увидим букву, а не \n + букву. Также непонятно, вот я ввёл символ ~, который в while отмечен по условию выхода. Почему тогда не производится выход из программы в тот момент, как я ввожу этот символ, если он уже добавился в буфер обмена, а в тот момент, когда я нажимаю Enter? Непонятно. 

Кроме того, мне непонятно, почему этот код даёт завершение ввода по нажатию ~:
Код

#include <stdio.h>

main()
{
  int a = getchar();

  while(a  != '~') {
    putchar(a);
    a = getchar();
    }
    putchar("\n");
}

а более короткая запись - уже нет:
Код

#include <stdio.h>

main()
{
  int a;

  while((a = getchar())  != '~') {
    putchar(a);
    }
    putchar("\n");
}


И почему в выводе первого варианта отсутствует перевод на новую строку и присутствует L?

Код

paulmad@bird:~/kernigan$ ./few_chars 
~
Lpaulmad@bird:~/kernigan$

Автор: baldina 21.8.2012, 03:19
putchar() считывает один символ. три путчара считывают 3 символа. но в программу какие-либо символы попадут только после нажатия Enter, т.к. ввод буферизуется.
так что в первом примере надо ввести abc<Enter> и считаются все три символа. непонятки с ~ по той же причине.

putchar("\n"); выведет странные вещи, т.к. ей передан не символ, а указатель на строку.

Автор: russians 21.8.2012, 04:08
baldina, стоп, а как же тогда работает getchar?
Ведь каждый цикл вызывается getchar, который считывает один символ из потока ввода, который, по определению K&R, являет собой последовательность символов, разбитых на строки. А мы вбиваем не по одному символу, а всю строку целиком и нажимаем Enter.
Как тогда понять конструкцию:

Код

while((a = getchar())  != '~') {
    putchar(a);
    }


Т.е. запускается getchar() и запускается ввод. Мы можем ввести целиком строку и нажимаем Enter. После этого getchar берёт введённую строку и посимвольно пробегается по ней? Непонятно, что если ввод строки открывает getchar, то функция должна завершить свою работу на этапе while и уничтожить строку, которая была введена, кроме первого символа, сохранённого в a, а её несуществующие символы обрабатываются в putchar. Что же тогда принимает строку на ввод из потока, если getchar принимает только один символ?

Автор: feodorv 21.8.2012, 04:16
А ещё можно сбрасывать буфер ввода:
Код

  while(a  != '~') {
    putchar(a);
    fflush( stdin );
    a = getchar();
    }
Но на "ввод" нажимать всё равно придётся)))

Автор: russians 21.8.2012, 12:43
feodorv, а разве вызов getchar() после putchar не опустошает буфер автоматически? Или буфер работает по принципу стека?

Автор: baldina 21.8.2012, 13:04
Цитата(russians @  21.8.2012,  04:08 Найти цитируемый пост)
по определению K&R, являет собой последовательность символов, разбитых на строки

Вы не совсем правильно поняли. Это не по их определению, так консоль устроена.

Цитата(russians @  21.8.2012,  12:43 Найти цитируемый пост)
Или буфер работает по принципу стека? 

буфер работает по принципу очереди

Цитата(russians @  21.8.2012,  04:08 Найти цитируемый пост)
getchar берёт введённую строку и посимвольно пробегается по ней

нет. getchar() извлекает один единственный символ из буфера (начала очереди). что и как попадает в буфер от программиста скрыто, известно только, что попадает со стандартного ввода.

программисту достаточно знать, что:
1. getchar() получает очередной символ из потока ввода (будет ждать, если доступных символов нет)
2. символы, вводимые с клавиатуры, станут доступны программе после нажатия клавиши enter.

необходимость нажатия enter связана только с механизмом работы консоли (по историческим ныне несущественным причинам).
если на стандартный ввод перенаправить из файла, будет как раз последовательная посимвольная обработка.

Добавлено @ 13:08
Цитата(russians @  21.8.2012,  12:43 Найти цитируемый пост)
разве вызов getchar() после putchar не опустошает буфер автоматически

нет. они вообще с разными потоками связаны, stdin и stdout.

Добавлено @ 13:11
попробуйте заменить В вашем примере getchar и putchar на getc и putc, может быть сразу станет понятнее.
Это суть одно и то же, только в getc и putc поток ввода/вывода указывается явно, а getchar и putchar однозначно работают с stdin и stdout соответственно. 

Автор: russians 21.8.2012, 13:33
baldina, спасибо за подробное разъяснение.
Но разве издательство K& покойный R (WPH) не могли добавить этот нюанс в новую редакцию, учитывая то, что с этим может возникнуть непонимание? Объяснить, что существует buffered input и unbuffered input.
А потом говорят, что этой книги вполне достаточно для базового понимания C smile

Автор: baldina 21.8.2012, 14:57
Цитата(russians @  21.8.2012,  13:33 Найти цитируемый пост)
для базового понимания C 

достаточно.
для базового понимания аппаратуры и ОС есть другие книги)))

Добавлено через 2 минуты и 12 секунд
Цитата(russians @  21.8.2012,  13:33 Найти цитируемый пост)
buffered input и unbuffered input.

в случае getc и getchar ввод всегда буферизирован, независимо консоль это или другой файл
в случае небуферизированного ввода с консоли enter все равно придется нажимать.
так что это к теме не относится))

Добавлено через 9 минут и 7 секунд
Буфер IO это сугубо программная вещь. Буферы, которые в драйверах (или в самой железке) совсем другого рода и уровня, Вы этим не можете управлять (посредством программного IO).
Представьте сетевой обмен. Например в Ethernet кадре данные это от 46 до 1500 байт. Можно читать/писать в сеть посимвольно, но это логически, с точки зрения программиста, а физически ходить данные все-равно будут порциями.

Автор: russians 21.8.2012, 15:27
baldina, ок, спасибо. Просто, в любом случае, человек наткнётся на эту проблему и ему будет непонятно, почему код, который он написал по заданию книги, не работает. Т.е. getchar() работает не просто так, он работает с системным буфером, и для цельного понимания нужно вводить элементы из устройства ОС Unix. Правда, у K&R для этого есть другая книжка smile

Автор: russians 21.8.2012, 16:35
baldina, а ещё такой вопрос: почему в варианте

Код

#include <stdio.h>
main()
{
  int a;
  while((a = getchar())  != EOF) {
    putchar(a);
    }
    putchar("\n");
}


текст вида aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^Z[Enter] прокатывает и вместо терминирования цикла - новая строка для ввода, а если напечатать одиночно ^Z - происходит терминирование цикла?

Просто в этом варианте: 

Код

#include <stdio.h>

main()
{
      int c, nl;
      
      nl = 0;
      
      while ((c = getchar()) != EOF)
            if (c == '\n')
               ++nl;
      printf("%d\n", nl);
      getchar();
}


мы считаем количество '\n', и тут getchar() доходит до конца строки. Почему в первом варианте не так?

Автор: baldina 21.8.2012, 19:01
пока enter не введен ^Z видимо просто сбрасывает буфер в программу (без enter), но не шлет конец файла. в отличие от ввода в начале строки

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)