#c #io #buffer #bufferedinputstream
Вопрос:
там я новичок в С. В настоящее время я читаю Kamp;R. Там меня смутило содержащееся в нем определение текстовых потоков: «Текстовый поток-это последовательность символов, разделенных на новые строки;каждая строка состоит из 0 или более символов, за которыми следует символ новой строки». И, пытаясь узнать об этих потоках, я познакомился с новым термином, а именно с буфером.
Я просто знаю, что:
- Непрерывный поток данных (байтов или символов) между устройствами ввода и вывода представляет
собой ПОТОК . - Область временного хранения в основной памяти для временного хранения входных или выходных данных представляет собой БУФЕР.
Я не говорю, что я прав, но это моя основная идея на этих условиях.
Я хочу знать, что такое на самом деле буфер и поток и как эти 2 вещи(т. Е. поток и буфер) работают вместе на неабстрактном уровне реализации C.
Ответ №1:
У вас есть три потока в C, stdin
, stdout
, и stderr
вы также можете думать о файлах , которые вы открыли, fopen
например, как о потоке. stdin
как правило, это клавиатура, stdout
как правило, ваш монитор, stderr
как правило, также и ваш монитор. Но это не обязательно так, это абстракции для аппаратного обеспечения.
Если, например, у вас не было клавиатуры, но, например, клавиатура в банковском банкомате, то stdin
это была бы клавиатура, если бы у вас не было монитора, а вместо этого был принтер, то stdout
это был бы принтер. Вы меняете оборудование, которое они используют, при вызовах вашей операционной системы. Вы также можете изменить их поведение, опять же, с помощью вызовов вашей операционной системы, что выходит за рамки того, о чем вы просите.
Таким образом, в некотором смысле, думайте о буфере как о памяти, выделенной операционной системой, связанной с потоком, для хранения данных, полученных от аппаратного компонента. Например, когда вы печатаете на клавиатуре, вводимые вами символы не захватываются непосредственно вашей средой разработки, они перемещаются оттуда в буфер, а затем вы считываете буфер.
Вот почему, например, вам нужно нажать enter, прежде чем ваш код начнет взаимодействовать с тем, что вы набрали на клавиатуре, потому stdin
что строка буферизована. Управление передается от вашей программы к операционной системе до тех пор, пока она не столкнется с чем-то, что отправляет управление обратно в вашу программу, в обычной ситуации это был бы символ новой строки.
Таким образом, в некотором смысле, думайте об этом так: поток-это устройство (клавиатура, монитор или файл на вашем жестком диске), буфер-это место, где хранятся данные, пока операционная система контролирует их, а затем вы взаимодействуете с буфером во время обработки данных.
Эта абстракция позволяет вам использовать все эти разные вещи общим способом, независимо от того, что они собой представляют, например: fgets(str, sizeof(str), STREAM)
… поток может быть любым входным потоком, будь то он stdin
или файл.
Делая еще один шаг вперед , вот почему новых программистов сбрасывают scanf
за an int
, за которым следует an fgets
, потому scanf
что считывает int
из буфера, но оставляет n
в буфере … затем вызов fgets
считывает n
то, что scanf
там осталось, и новому программисту остается только гадать, почему они не смогли ввести какие-либо данные. Таким образом, ваше любопытство к потокам и буферам сослужит вам хорошую службу по мере продвижения вперед в изучении C.
Комментарии:
1. Эй @LEF, спасибо, чувак, твой ответ заставил меня наконец прийти к выводу. Это мне очень помогло!
2. Эй, не могли бы вы предложить мне книгу, которая поможет мне узнать более глубокие реализации C (то есть узнать, как это реальные абстракции работают за кулисами).
3. @UdayKiran все, что я когда-либо использовал, — это Язык программирования Си, в этой маленькой книжке целая жизнь обучения. Но иногда это слишком «высокий уровень», поэтому копайтесь в ядре Linux. Он написан на C, и, поскольку он с открытым исходным кодом, вы действительно можете посмотреть, как реализуются абстракции. Вы можете видеть, что происходит за кулисами
fgets
, например, когда вы звоните. Если вы ищете интересный проект, напишите настройку сервера / клиента чата, воспользуйтесь сетевым руководством Beej, оно поможет вам освоить программирование на Linux (и расширить ваши знания о потоках).4. @UdayKiran с точки зрения серверной / клиентской программы, углубитесь в функцию выбора, вы даже можете написать небольшую игру или что-то в этом роде, используя функцию timer_fd ядра Linux, затем с точки зрения документации для изучения вы просто используете руководство программиста linux (справочные страницы) вместе с различными ресурсами в Интернете. Вы увидите, что stdin просто имплантирован как файловый дескриптор, и так далее, и тому подобное. Повеселиться.
5. @UdayKiran допустил опечатку в приведенном выше комментарии, и время для редактирования истекло, просто чтобы было ясно, что это timerfd, а не timer_fd
Ответ №2:
Определения не плохие, на самом деле очень хорошие. Вы могли бы добавить (с объектно-ориентированной точки зрения), что ПОТОК использует БУФЕР.
Использование БУФЕРА может потребоваться, например, по соображениям производительности, поскольку каждый системный вызов сопряжен с относительно высокой стоимостью.
Особенно системные вызовы ввода-вывода, жесткий диск или доступ к сети выполняются медленно по сравнению со временем доступа к памяти. И они складываются, если чтение или запись состоят только из одного байта.
Ответ №3:
Двумя общими абстракциями устройств ввода-вывода являются:
Потоки — передает переменное количество байтов по мере готовности устройства.
Блок — перенос записей фиксированного размера.
Буфер — это просто область памяти, в которой хранятся передаваемые данные.
Ответ №4:
На самом деле это довольно хорошие рабочие определения.
В практических терминах языка Си буфер-это массив (обычно типа char
или unsigned char
), который используется для хранения данных либо в результате операции ввода, либо перед отправкой на вывод. Массив может быть объявлен как массив фиксированного размера, например
char buffer[SOME_BUFFER_SIZE];
или динамически, используя
char *buffer = malloc( SOME_BUFFER_SIZE * sizeof *buffer );
Преимущество использования динамической памяти заключается в том, что при необходимости размер буфера можно изменять; недостатком является то, что вам приходится управлять временем жизни этой памяти.
Для ввода/вывода текста вы обычно используете массивы char
; для двоичного ввода/вывода вы обычно используете массивы unsigned char
.
Системы, взаимодействующие по сети, довольно часто отправляют данные «кусками» фиксированного размера, так что для передачи всех данных может потребоваться несколько операций чтения или записи. Подумайте о веб — сервере и браузере-сервер отправляет HTML в нескольких сообщениях, а браузер сохраняет промежуточный результат в буфере. Только после того, как все данные будут получены, браузер отобразит страницу:
Received from Web server Stored in browser's input buffer
------------------------ --------------------------------
HTTP/1.1 200 OK rn <!DOCTYPE HTML><html
Content-length: 20rn
<!DOCTYPE HTML><html
HTTP/1.1 200 OK rn <!DOCTYPE HTML><html><head><title>This i
Content-length: 20rn
><head><title>This i
HTTP/1.1 200 OK rn <!DOCTYPE HTML><html><head><title>This i
Content-length: 20rn s a test</title></he
s a test</title></he
HTTP/1.1 200 OK rn <!DOCTYPE HTML><html><head><title>This i
Content-length: 20rn s a test</title></head><body><p>Hello, W
ad><body><p>Hello, W
HTTP/1.1 200 OK rn <!DOCTYPE HTML><html><head><title>This i
Content-length: 19 s a test</title></head><body><p>Hello, W
orld!</body></html> orld!</body></html>
Ни один нормальный сервер не отправляет HTML фрагментами по 20 символов, но это должно проиллюстрировать, почему и как используются буферы.