Урок 1. Часть 3: Инструкции языка Си scanf, gets, getchar — ввод данных
Функция языка Си gets
Ввод данных — это предоставление компьютеру информации, необходимой для работы программы. Вводимая информация присваивается переменным и хранится в памяти компьютера. В программе переменная используется в операциях и выражениях.
Функция Си gets() помещает в переменную вводимую строку. В качестве параметра функции указывается имя переменной, в которую и помещается строка.
var name:string; begin writeln(‘введите имя’); readln(name); writeln(name); end.
Функция языка Си getchar
Функция Си getchar() запрашивает с клавиатуры единичный символ и практически для всех компиляторов безразлично, к какому типу (char или int) относится данный символ.
Вызов функции getchar осуществляется другим способом. Параметра у нее нет, но указать круглые пустые скобки после имени функции надо обязательно. Вызов происходит как присваивание какой-то переменной значения функции. Т.е. слева указывается переменная и ей присваивается значение функции, указанной справа.
Для сравнения выполним ту же задачу с функцией Си gets():
Функция языка Си scanf
Функция Си scanf () является более многогранной функцией, так как позволяет вводить в компьютер данные любых типов.
По аналогии с функцией printf() функция Си scanf() тоже может иметь несколько аргументов, позволяя тем самым вводить значения числовых, символьных и строковых переменных в одно и то же время.
Список параметров функции scanf() состоит из двух частей: строки формата и списка данных. В строке формата находятся указатели формата (преобразователи символов, после знака процента), которые определяют то, каким образом должны быть интерпретированы вводимые данные. Вторая часть функции — список данных, в котором перечисляются переменные, в которые должны быть занесены вводимые значения. Порядок следования указателей формата и переменных должен быть одинаков.
Указатели формата аналогичны тем, которые используются функцией printf.
getchar, getwchar
Считывает символ из стандартного входного потока.
Синтаксис
Возвращаемое значение
Возвращает считанный символ. Эти функции ожидают ввода и не возвращают, пока не будут доступны входные данные.
Чтобы указать на ошибку чтения или конец файла, функция GetChars возвращает значение EOF, а getwchar возвращает WEOF. Для GetCharsиспользуйте ferror или feof для проверки ошибки или завершения файла.
Комментарии
Каждая подпрограммы считывает один символ из stdin и увеличивает связанный указатель файла, чтобы он указывал на следующий символ. Функция GetChars аналогична _fgetchar, но реализована в виде функции и макроса.
Эти функции также блокируют вызывающий поток и являются потокобезопасными. Описание неблокирующей версии см. в разделе _getchar_nolock, _getwchar_nolock.
По умолчанию глобальное состояние этой функции ограничивается приложением. Чтобы изменить это, см. раздел глобальное состояние в CRT.
Универсальное текстовое сопоставление функций
| Подпрограмма TCHAR.H | &Не определено _UNICODE _MBCS | _MBCS определено | _UNICODE определено |
|---|---|---|---|
| _gettchar | getchar | getchar | getwchar |
Требования
| Подпрограмма | Обязательный заголовок |
|---|---|
| getchar | |
| getwchar | или |
консоль не поддерживается в приложениях универсальная платформа Windows (UWP). Стандартные дескрипторы потока, связанные с консолью, stdin, stdoutи stderr, должны быть перенаправляться до того, как функции времени выполнения C смогут использовать их в приложениях UWP. Дополнительные сведения о совместимости см. в статье Compatibility.
Посимвольный ввод и вывод. Понятие буфера
Функции putchar() и getchar()
Функция putchar() обычно принимает в качестве аргумента символ, либо символьную переменную и в результате своей работы выводит соответствующий символ на экран. Однако этой функции можно передать любое целое число, но, понятное дело, символа на экране вы можете не получить, если числу не соответствует ни один символ по таблице ASCII. Например:
Функции putchar() и printf() в определенном смысле взаимозаменяемы, так как, используя ту или другую, можно получить один и тот же результат. Хотя программный код будет выглядеть по-разному:
В результате выполнения этого кода на экране будут напечатаны два слова «Hello», разделенные переходом на новую строку. С putchar() это выглядит несколько сложнее. Как мы знаем, любая строка оканчивается нулевым по таблице ASCII символом, в данном случае этот символ служит сигналом для прекращения вывода на экран. Но если бы понадобилось вывести на экран строку, разделяя ее символы каким-нибудь другим символом (например, тире), то и в случае с printf() было бы не так все просто:
Поэтому выбор в пользу той или иной функции зависит от ситуации и ваших предпочтений.
В отличие от функции putchar() функция getchar() не имеет параметром. Когда getchar() выполняется, она считывает их потока ввода один символ и возвращает его в программу. Полученный таким образом символ может быть присвоен переменной, участвовать в выражениях или выводиться на экран с помощью функций вывода.
Если при выполнении этого кода ввести символ, то после нажатия Enter вы увидите два таких же символа на экране:
Прежде чем попытаться найти объяснение, изобразим «некорректного пользователя» и перед первым нажатием Enter введем несколько символов (больше двух). После Enter вы увидите три первых символа введенной вами строки, и программа завершиться. Хотя куда логичней было бы ожидать, что будет прочитан только первый символ, потом выведен на экран и потом запрошен следующий символ.
Такое странное на первый взгляд поведение программы связано не с языком C, а с особенностью работы операционных систем, в которых реализован буферный ввод-вывод. При операциях ввода-вывода выделяется область временной памяти (буфер), куда и помещаются поступающие символы. Как только поступает специальный сигнал (например, переход на новую строку при нажатии Enter), данные из буфера передаются по месту своего назначения (на экран, в переменную и др.).
Теперь, зная это, давайте посмотрим, что происходило в нашей программе, и сначала разберем второй случай с «некорректным пользователем», т.к. для понимания этот случай проще. Когда пользователь ввел первый символ, он попал в переменную a, далее сработала функция putchar(a) и символ попал в буфер. Т.к. Enter’а не было, то содержимое буфера на экране не было отображено. Пользователь ввел второй символ, переменная b получила свое значение, а putchar(b) отправила это значение в буфер. Аналогично с третьим символом. Как только пользователь нажал Enter, содержимое буфера было выведено на экран. Но символы, которые были выведены на экран, были выведены не программой, а операционной системой. Программа же выводила символы еще до того, как мы нажали Enter.
Почему же в первом случае при выполнении программы мы смогли ввести и увидеть на экране только два символа? Когда был введен первый символ, то он был присвоен переменной a и далее выведен в буфер. Затем был нажат Enter. Это сигнал для выброса данных их буфера, но это еще и символ перехода на новую строку. Этот символ ‘\n’, наверное, и был благополучно записан в переменную b. Тогда в буфере должен оказаться переход на новую строку, после этого введенный символ (уже помещенный в переменную c). После нажатия Enter мы должны были бы увидеть переход на новую строку от символа ‘\n’ и букву. Однако печатается только буква. Причина этого вероятно в том, что переход на новую строку не хранится в буфере.
Во многих учебниках по языку C приводится пример считывания символов, вводимых пользователем, и их вывод на экран:
В переменной a всегда хранится последний введенный символ, но перед тем как присвоить a новое значение с помощью функции putchar() старое значение сбрасывается в буфер. Как только поступает символ новой строки, работа программы прекращается, а также, поскольку была нажата клавиша Enter, происходит вывод содержимого буфер на экран. Если в условии цикла while будет не символ ‘\n’, а какой-нибудь другой, то программа будет продолжать обрабатывать символы, даже после нажатия Enter. В результате чего мы можем вводить и выводить множество строк текста.
Напишите программу посимвольного ввода-вывода, используя в качестве признака окончания ввода любой символ, кроме ‘\n’. Протестируйте ее.
При совместном использовании функций putchar() и getchar() обычно пользуются более коротким способом записи. Например:
Как быть, если требуется «прочитать» с клавиатуры или файла неизвестный текст, в котором может быть абсолютно любой символ? Как сообщить программе, что ввод окончен, и при этом не использовать ни один из возможных символов?
В операционных системах GNU/Linux можно передать функции getchar() значение EOF, если нажать комбинацию клавиш Ctrl + D, в Windows – Ctrl + Z.
Исправьте вашу программу таким образом, чтобы считывание потока символов прерывалось признаком EOF.
Решение задач
Здесь ввод символов может прекратиться не только при поступлении значения EOF, но и в случае, если массив заполнен ( i ). В цикле while проверяется условие, что числовой код очередного символа принадлежит диапазону [48, 57]. Именно в этом диапазоне кодируются цифры (0-9). Если поступивший символ является символом-цифрой, то он помещается в массив по индексу i, далее i увеличивается на 1, указывая на следующий элемент массива. После завершения цикла к массиву символов добавляется нулевой символ, т.к. по условию задачи должна быть получена строка (именно для этого символа ранее резервируется одна ячейка массива – N-1 ).
Getchar в си что это
В вычислительной технике слова «ввод» и «вывод» применяются в нескольких разных смыслах. Мы можем говорить об устройствах ввода и вывода, таких, как терминалы, накопители на магнитных дисках, точечно-матричные принтеры и т. п., или о данных, используемых при вводе и выводе, или же, наконец, о функциях, реализующих ввод и вывод. Основной целью данной главы является обсуждение функций, применяемых при вводе и выводе, но, кроме этого, мы коснемся и двух других аспектов этого понятия.
Под функциями ввода-вывода подразумеваются функции, которые выполняют транспортировку данных в программу и из нее. Мы уже использовали две такие функции: printf( ) и scanf( ). Теперь же рассмотрим несколько других возможностей, предоставляемых языком Си.
Функции ввода-вывода не входят в определение языка Си; их разработка возложена на программистов, реализующих компилятор с языка Си. Если вы являетесь проектировщиком такого компилятора, то можете реализовать любые функции ввода-вывода. Если вычислительная система, для которой вы его создаете, обладает той или иной особенностью, например тем, что каналы ввода-вывода построены на основе портов микропроцессора INTEL 8086, вы можете встроить в нее специальные функции ввода-вывода, ориентированные на эту особенность. Мы рассмотрим пример применения такого подхода в конце данной главы. С другой стороны, выгода использования стандартного набора функций ввода-вывода на всех системах очевидна. Это дает возможность писать «переносимыe» программы, которые легко можно применять на разных машинах. В языке Си имеется много функций ввода-вывода такого типа, например printf( ) и scanf( ). Ниже мы рассмотрим функции getchar( ) и putchar( ).
Эти две функции осуществляют ввод и вывод одного символа при каждом обращении к ним. На первый взгляд, выполнение операций подобным образом может показаться довольно странным так как, учитывая все сказанное выше, мы уже можем с легкостью осуществить ввод нескольких символов подряд. Но этот способ ввода данных лучше соответствует возможностям машины. Более того, такой подход служит основой построения большинства про грамм обработки текстов, являющихся последовательностями обычных слов. Мы увидим, как можно применять эти функции в программах, занимающихся подсчетом символов, чтением и копированием файлов. Попутно мы узнаем про буферы, эхо-печать и переключение ввода-вывода.
РИС. 6.1. Функции getchar( ) и putchar( ) рабочие лошади программы обработки текстов
Функция getchar() получает один символ, поступающий с пульта терминала (и поэтому имеющий название), и передает его выполняюшейся в данный момент программе. Функция putchar( ) получает один символ, поступающий из программы, и пересылает его для вывода на экран. Ниже приводится пример очень простой программы. Единственное, что она делает, это принимает один символ с клавиатуры и выводит его на экран. Мы будем постепенно модифицировать данную программу до тех пор, пока она не приобретет ряд полезных возможностей. Из дальнейшего вы узнаете, что представляют из себя эти возможности, но сначала давайте посмотрим на наш скромный первый вариант
Для большинства систем спецификации функций getchar и putchar содержатся в системном файле stdio.h, и только по этой причине мы указали данный файл в программе. Использование такой программы приводит к следующему: или, возможно, к
Обозначение [ввод] служит указанием, что вы должны нажать клавишу [ввод]. В любом случае, первый символ g вы набираете на клавиатуре сами, а второй выводится компьютером.
Результат зависит от того, есть в вашей системе «буферизованный» ввод или нет. Если перед тем как получить на экране ответ, вы должны нажать клавишу [ввод], то буферизация в вашей системе имеется. Давайте закончим рассмотрение функций getchar( ) и putchar( ) перед тем, как приступить к обсуждению понятия буферов.
Функция getchar( ) аргументов не имеет (т. е. при ее вызове в круглые скобки не помещается никакая величина). Она просто получает очередной поступающий символ и сама возвращает его значение выполняемой программе. Например, если указанная функция получает букву Q, ее значением в данный момент будет эта буква. Оператор, приведенный в строке 1, присваивает значение функции getchar( ) переменной ch.
Функция putchar( ) имеет один аргумент. При ее вызове необходимо в скобках указать символ, который требуется вывести на печать. Аргументом может быть одиночный символ (включая знаки представляемые управляющими последовательностями, описаными в гл. 3), переменная или функция, значением которой является одиночный символ. Правильным обращением к функции putchar( ) является указание любого из этих аргументов при ее вызове.
Форму записи, приведенную в последнем примере, мы можем использовать для того, чтобы представить нашу программу в следующем виде:
Такая запись очень компактна и не требует введения вспомогательных переменных. Кроме того, в результате компиляции такая программа оказывается более эффективной, но, пожалуй, менее понятной.
После того как мы ознакомились с работой этих двух функций, можно перейти к обсуждению понятия буферов.
При выполнении данной программы (любой из двух ее версий) вводимый символ в одних вычислительных системах немедленно появляется на экране («эхо-печать»), в других же ничего не происходит до тех пор, пока вы не нажмете клавишу [ввод]. Первый случай относится к так называемому «небуферизованному» («прямому») вводу, означающему, что вводимый символ оказывается немедленно доступным ожидающей программе. Второй случай служит примером «буферизованного» ввода, когда вводимые символы собираются и помешаются в некоторую область временной памяти, называемую «буфером». Нажатие клавиши [ввод] приводит к тому, что блок символов (или один символ) становится доступным программе. В нашей программе применяется только первый символ, поскольку функция getchar( ) вызывается в ней один раз. Например, работа нашей программы в системе, использующей буферизованный ввод, будет выглядеть следующим образом:
В системе с небуферизованным вводом отображение на экране символа В произойдет сразу, как только вы нажмете соответствующую клавишу. Результат ввода-вывода при этом может выглядеть, например, так:

РИС. 6.2. Схема буфернзованного и небуферизованного ввода
Однако для некоторых диалоговых программ небуферизованный ввод может оказаться приемлемым. Например, в программах обработки текстов было бы желательно, чтобы каждая комманда вводилась, как только вы нажимаете соответствующую клавишу. Поэтому как буферизованный, так и небуферизированный ввод имеет свои достоинства.
В данном примере была использована структура программы, обсуждавшаяся нами в конце гл. 5 (вопрос 3). При первом прохождении тела цикла функция putchar() получает значение своего аргумента в результате выполнения оператора, расположенного в строке 9; в дальнейшем, вплоть до завершения работы цикла, значением этого аргумента является символ, передаваемый программе функцией getchar( ), расположенной в строке 12. Мы ввели новую операцию отношения !=, смысл которой выражается словами «не равно». В результате всего этого цикл while будет осуществлять чтение и печать символов до тех пор, пока не поступит признак STOP. Мы могли бы опустить в программе директиву #define и использовать лишь символ * в операторе while, но наш способ делает смысл данного знака более очевидным.
Перед тем как приступить к выполнению этой замечательной программы на своей машине, взгляните на ее следующий вариант. Программа, приведенная ниже, делает то же самое, но стиль ее написания лучше отвечает духу языка Си:
Одна строка 8 этой программы заменяет строки 9, 10 и 12 программы ввод-вывод2. Как же работает этот оператор? Начнем с того, что рассмотрим содержимое внутренних скобок:
Тем самым выполняется проверка, которую в программе ввод-вывод2 осуществлял оператор, расположенный в строке 10. Конструкции подобного сорта (объединение в одном выражении операций присваивания и сравнения) довольно часто используются при программировании на языке Си:
Аналогично нашему предыдущему примеру, в котором применя лась конструкция while (++ size Далее Содержание
Какой это даст эффект? Очень большой ведь символ «новая стрoка» пересылается при нажатии клавиши [ввод], следовательно, в результате наша программа будет обрабатывать одну вводимую строку. Предположим, например, что мы внесли указанное изменение в программу подсчет символов1, а затем при ее выполнении ввели следующую строку: В ответ на экране появятся следующие строки
(Если бы мы не включили в управляющую строку оператора printf( ) в качестве первого символа признак \n, второе сообщение появилось бы справа от запятой, после слова лето. Мы предпочли избежать такого склеивания строк).
Признак, появляющийся в результате нажатия клавиши [ввод] не входит в число символов (43), подсчитанных программой, поскольку подсчет осуществляется внутри цикла.
Теперь у нас есть программа, которая может прочесть одну строку. В зависимости от того, какой оператор помещен в тело цикла while, программа может осуществлять эхо-печать, подсчет числа символов в строке или и то и другое. Эти средства представляются нам в принципе полезными, скажем как часть некоторой большей программы. Но было бы хорошо иметь возможность читать большие порции данных, например файл данных. Это может быть осуществлено путем надлежащего выбора признака STOP.
Каким может быть идеальный признак STOP? Это должен быть такой символ, который обычно не используется в тексте и следовательно, не приводит к ситуации, когда он случайно встретится при вводе, и работа программы будет остановлена раньше чем мы хотели бы.
Проблема подобного сорта не нова, и, к счастью для нас, она уже была успешно решена проектировщиками вычислительных систем. На самом деле задача, которую они рассматривали, была не сколько отличной от нашей, но мы вполне можем воспользоваться их решением. Занимавшая их проблема касалась «файлов». Файлом можно назвать участок памяти, в который помещена некоторая информация. Обычно файл хранится в некоторой долговременной памяти, например на гибких или жестких дисках или на магнитной ленте. Чтобы отмечать, где кончается один файл и начинается другой, полезно иметь специальный символ, указывающий на конец файла. Это должен быть символ, который не может появиться где-нибудь в середине файла, точно так же как выше нам требовался символ, обычно не встречающийся во вводимом тексте. Решением указанной проблемы служит введение специального признака, называемого «End-of-File» (конец файла), или EOF, для краткости. Выбор конкретного признака EOF зависит от типа системы он может состоять даже из нескольких символов. Но такой признак всегда существует, и компилятор с языка Си, которым вы пользуетесь, конечно же «знает», как такой признак действует в вашей системе.
РИС. 6.4. Структура текстового файла с признаком EOF
Это дает возможность использовать в программах выражения, подобные, например, такому
Поэтому мы можем переписать нашу предыдущую программу, осущecтвляющyю ввод и эхо-печать символов, так:
Отметим следующие моменты:
1. Нам не нужно самим определять признак EOF, поскольку заботу об этом берет на себя файл stdio.h.
2. Мы можем не интересоваться фактическим значением символа EOF, поскольку директива #define, имеющаяся в файле stdio.h, позволяет нам использовать его символическое представление.
3. Мы изменили тип переменной ch с char на int. Мы поступили так потому, что значениями переменных типа char являются целые числа без знака в диапазоне от 0 до 255, a признак EOF может иметь числовое значение -1. Эта величина недопустима для переменной типа char, но вполне подходит для переменной типа int. К счастью, функция getchar() фактически возвращает значение типа int, поэтому она в состоянии прочесть символ EOF.
4. Переменная ch целого типа никак не может повлиять на работу функции putchar( ). Она просто выводит на печать символьный эквивалент значения аргумента.
5. При работе с данной программой, когда символы вводятся с клавиатуры, необходимо уметь вводить признак EOF. He думайте, что вы можете просто указать буквы E-О-F или число -1. (Число -1 служит эквивалентом кода ASCII данного символа, а не самим этим символом. Вместо этого вам необходимо узнать, какое представление используется в вашей системе. В большинстве реализаций операционной системы UNIX, например, ввод знака [CTRL/d] (нажать на клавишу [d], держа нажатой клавишу [CTRL]) интерпретируется как признак EOF. Во многих микрокомпьютерах для той же цели используется знак [CTRL/z].
Приведем результат 1) работы программы ввод-вывод4 в системе, обладающей буферизованным вводом:
Каждый раз при нажатии клавиши [ввод] производится обработка символов, попавших в буфер, и копия строки выводится на печать. Это продолжается до тех пор, пока мы не введем признак EOF.
Существуют два способа написания программ, работающих с файлами. Первый способ заключается в явном использовании специальных функций, которые открывают и закрывают файлы, организуют чтение и запись данных и т. п.; мы не хотим пока касаться этого вопроса. Второй способ состоит в том, чтобы использовать программу, спроектированную первоначально в предположении что данные в нее вводятся с клавиатуры и выводятся на экран, переключить ввод и вывод на другие информационные каналы: например, из файла в файл. Этот способ в некоторых отношениях обладает меньшими возможностями, чем первый, но зато гораздо проще в использовании. Мы изучим понятие переключения в данном разделе.
Предположим, вы осуществили компиляцию программы ввод-вывод4 и поместили выполняемый объектный код в файл с именем getput4. Затем, чтобы запустить данную программу, вы вводите с терминала только имя файла
Но мы, конечно, не можем гарантировать, что в файле, который выберете вы, тоже окажется четверостишие Вильяма Блейка.
Теперь предположим (если вы еще не устали и в состоянии что-нибудь предположить), вам хочется, чтобы слова, вводимые с клавиатуры, попадали в файл с именем mywords. Для этого вы должны ввести команду
и начать ввод символов. Символ > служит обозначением еще одной операции переключения, используемой в ОС UNIX. Ее выполнение приводит к тому, что создается новый файл с именем mywords, а затем результат работы программы ввод-вывод4, представляющий собой копию вводимых символов, направляется в данный файл. Если файл с именем mywords уже существует, он обычно уничтожается, и вместо него создается новый. (В некоторых реализациях ОС UNIX, однако, вам предоставляется возможность защитить существующие файлы.) На экране в данном случае появятся лишь вводимые вами символы; их же копии будут направлены в указанный файл. Чтобы закончить работу программы, введите признак EOF; в системе UNIX это обычно символ [CTRL/d]. Попробуйте воспользоваться описанной здесь операцией. Если вам ничего другого не придет в голову, просто воспроизведите на своей машине пример, приведенный ниже. Знак приглашения, выводимый на экран интерпретатором команд SHELL, обозначается здесь символом %. Не забывайте оканчивать каждую введенную строку символом [возврат], чтобы содержимое буфера пересылалось в программу.
у вас не должно быть трудностей с запоминанием того, какая операция переключения для чего предназначена. Необходимо помнить только, что знак каждой операции указывает на направление информационного потока. Вы можете по ассоциации представлять себе этот знак в виде воронки. [CTRL/d]
После того как введен символ [CTRL/d], программа заканчивает свою работу и возвращает управление операционной системе UNIX, на что указывает повторное появление знака приглашения. Как убедиться в том, что наша программа вообще работала? В ОС UNIX существует команда Is, которая выводит на экран имена файлов; обращение к ней должно продемонстрировать вам, что файл с именем mywords теперь существует. Чтобы проверить его содержимое, вы можете воспользоваться командой cat или запустить заново программу ввод-вывод4, направляя в нее на этот раз содержимое входного файла.
У вас не должно быть трудностей с запоминанием того, какая операция переключения для чего предназначена. Необходимо помнить только, что знак каждой операции указывает на направление информационного потока. Вы можете по ассоциации представлять себе этот знак в виде воронки.
Предположим теперь, что вы хотите создать копию файла mywords и назвать ее savewords. Введите для этого команду и требуемое задание будет выполнено. Команда
приведет к такому же результату, поскольку порядок указания операций переключения не имеет значения. Нельзя использовать в одной команде один и тот же файл и для ввода и для вывода одновременно.
Причина этого заключается в том, что указание операци, > mywords приводит к стиранию исходного файла перед его использованием в качестве входного.
РИС. 6.5. Комбинированное переключение.
Рассмотрим еще один пример: напишем очень простую программу, шифрующую сообщения; с этой целью мы немного изменим программу ввод-вывод4 и получим
Потому что в тексте, содержащемся в файле original, в конце первой строки находиться символ «новая строка», служащий указанием компьютеру начать вывод следующего слова с новой строки. Но этот символ также был изменен. В нашей системе он был заменен символом ^К, являющимся аналогом специального символа [CTRL/k], и поэтому последующий вывод на печать был продолжен на прежней строке. Если мы хотим иметь программу шифровки сообщений, сохраняющую первоначальную структуру текста по строкам), нам необходимо средство, позволяющее изменять все символы, кроме символа «новая строка». В следующей главе мы узнаем, как это сделать.
Здесь мы главным образом рассмотрим, чем отличаются другие операционные системы от ОС UNIX; поэтому если вы пропустили предыдущий раздел, вернитесь назад и прочтите его.
Все отличия можно разделить на две группы:
1. В других операционных системах реализована операция переключения.
2. Компиляторы с языка Си предоставляют возможность использовать операцию переключения.
Мы не можем рассмотреть все возможные операционные системы, поэтому приведем пример только одной из них, но весьма широко распространенной. Это система MS-DOS 2; она вначале была просто «отпрыском» ОС СР/М, а сейчас самостоятельно развивается в сторону операционной системы XENIX, подобной ОС UNIX. В версию MS-DOS были введены операции переключения ; они работают в ней точно так же, как было описано в предыдущем разделе.
Переключение вывода в файл: >
Что вы можете делать с помощью этой программы? Можете просто игнори ровать ее, или же (переписав ее по другому) изменять вид фигур которые она выводнт на печать, либо наконец искать такие комбинации входных символов, что в результате на экране будут появляться привлекательные фигуры например при вводе такой последовательности: Результат работы программы будет выглядеть так
Рассмотрим различные устройства ввода-вывода, поскольку теперь мы хотим обсудить вопрос о том, как приспособить реализацию компилятора с языка Си к требованиям конкретной вычислительной системы. Многие из современных микрокомпьютеров спроектированы на основе микропроцессорных интегральных схем (ИС) INTEL 8086 и INTEL 8088. Наиболее известным является персональный компьютер IBM PC, в котором применяются ИС второго типа. Конкретный пример, который приведен ниже, относится к упомянутому компьютеру, но обсуждаемые принципы применимы и при рассмотрении других мини-машин, построенных на базе семейства микропроцессоров 8086/8088.
Порт 97 не управляет непосредственной работой громкоговорителя. Устройство, осуществляющее эти функции, называется «Программируемый параллельный интерфейсный контроллер 8255». Этот микропроцессор имеет три «регистра» (небольших, легко до ступных элемента памяти), в каждом из которых содержится некоторое число. Числа в регистрах используются для управления работой данного устройства. Каждый регистр связан с ИС 8088 через порт, и регистру, управляющему громкоговорителем, выделен для связи порт 97. С его помощью управление данным устройством осуществляется путем изменения числа в регистре. При посылке правильного» числа громкоговоритель издает звуковой сигнал; посылка же «неправильного» числа может вызвать ряд проблем. Поэтому нам необходимо знать, какие числа требуется посылать и как их нужно посылать. В частности, нам хотелось бы знать, как использовать язык Си для подобного рода операций.
РИС. 6.6. Связь контроллера 8255 с микропроцессором INTEL 8088.
Обратите внимание на знаки + и — на рис. 6.7. Знак + указывает, что в соответствующем разряде выполнение условия обозначается через 1, а знак — указывает, что выполнение условия в разряде обозначается через 0. Поэтому 1 в 3-м разряде показывает, что двигатель накопителя на мини-кассете выключен, в то время как 0 в 4-м разряде указывает на возможность доступа к памяти.
Каким образом можно включить громкоговоритель? Оказывается, для этого необходимо в 0-й разряд (включение громкоговорителя через таймер 2) и в 1-й разряд (наличие данных для работы громкоговорителя) заслать 1. Это означает, что для включения громкоговорителя через порт 97 необходимо послать в регистр двоичное число 11 (или десятичное число 3). Но, перед тем как приступить к этому, учтите, что данная операция имеет такие побочные эффекты, как, например, установка разряда 4 в 0, что может оказаться вовсе нежелательным. Одна из причин, по которой мы не рассказали, как использовать порты, заключается в том, чтобы предотвратить неприятные последствия вашей поспешности.
Для надежности мы должны проверить сначала, что содержится в регистре. К счастью, это совсем не трудно (мы продемонстрируем это чуть позже). Ответ выглядит так: в регистре обычно содержатся числа «76» или «77 «. Давайте переведем их в двоичную систему. (Здесь вам, возможно, захочется заглянуть в таблицу преобразования в двоичный код, которая приводится в конце книги в приложении.) Результаты преобразования некоторых чисел приве-дены в табл. 6.1.
Не вдаваясь в подробности по поводу значения слов «поддержание низкой тактовой частоты задающего генератора клавиатуры
Таблица 6.1.
| Десятичное число | Номер разряда | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 76 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | |
| 77 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | |
| 78 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | |
| 79 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 |
Можно сказать, что надежный способ выполнения указанной операции заключается в том, чтобы оставить без изменения значения всех разрядов, кроме нулевого и первого. Это достигается путем передачи в регистр двоичного числа 0100111 (или десятичного 79). В качестве дополнительных мер предосторожности мы должны запомнить исходное значение, содержащееся в регистре, а затем после звукового сигнала громкоговорителя восстановить содержимое указанного регистра. (Битовые операции, рассматриваемые в приложении в конце данной книги, предоставляют другую возможность для занесения некоторого значения в регистр.) Теперь мы готовы к тому, чтобы заставить громкоговоритель подать звуковой сигнал.
Существуют две операции, которые могут выполняться с помощью порта: микропроцессор 8088 может послать информацию в подсоединенное устройство или прочитать данные из него. В языке Асемблера эти операции выполняются при помощи команд OUT и IN, а в языке Си использование указанных средств зависит от компилятора. Некоторые из них предоставляют возможность вызова специальных функций (в соответствии с тем, как это обычно делается в языке Си). В компиляторах Lattice С и Supersoft С, например с этой целью применяются функции outp( ) и inp( ), в других же аналогичные функции могут носить другие имена. Если вы работаете с компилятором, в котором такие возможности отсутствуют для задания указанных функций можно либо воспользоваться ассемблером, либо просто включить в свою программу соответствующий ассемблерный код (что очень просто). В любом случае вам нообходимо ознакомиться с документацией по вашему компилятору. Пока же будем предполагать, что у вас имеется возможность вызова функций outp( ) и inp( ).
Приведем пример программы, представляющей собой первую попытку извлечь звуковой сигнал из громкоговорителя:
Несмотря на то что, по-видимому, вы и сами можете догадаться, что выполняют функции inp( ) и outp( ), ниже приведем их формальное описание:
| inр(номер порта) |
| оuр(номер порта, значение) |
Давайте теперь выполним программу. В итоге вы можете быть не совсем удовлетворены, поскольку компьютер выключает громкоговоритель довольно быстро после включения. Было бы лучше если бы мы смогли заставить компьютер подождать немного, прежде чем выключить громкоговоритель. Как это можно сделать? Довольно просто! Нужно только дать компьютеру какую-нибудь работу» на это время. Приведенная ниже программа показывает, как этого достичь.
Было бы прекрасно иметь возможность регулировать и высоту тона. Это и в самом деле осуществимо. После того как мы изучим функции более полно, в приложении в конце книги вы сможете познакомиться с программой, которая превращает клавиатуру терминала в клавиатуру музыкального инструмента.
Хотите узнать чудовищный потенциал машины для «перемалы вания чисел»? Как раз для этого мы написали замечательную программу (приведенную на рис 6.8). Чтобы оценить ее полностью вам необходимо выполнить ее на вашем компьютере. Предупреждение для получения желаемого эффекта вы должны выбрать подходящую для вашей системы величину константы LIMIT. Дополнительные подробности будут обсуждены ниже, а сначала рассмотрим саму программу РИС. 6.8. Программа для «перемалывания чисел»
Технические замечания операторы while, в которых содержится переменная delay, не делают ничего другого, кроме организации ЗАДЕРЖКИ по времени Символ «точка с запятой» в конце строки показывает на конец тела цикла while, т.е. последующие операторы в него не входят. Цикл while, использованный внутри другого цикла whi1е, называется «вложенным». Мы полагаем, что на IBM PC подходящим значением для константы LIMIT является число 8000 АХ 11/750 мы предпочитаем число порядка 50000, но на выбор может влиять также уровень загрузки системы, работающей в режиме разделения времени. Мы полагаем LIMIT равной значению константы типа long (как раз на это и указывает символ L, стоящий в конце) для того, чтобы избежать трудностей, связанных с превышением максимального значения величины типа int (Для 8000 подобные меры предосторожности на самом деле обязательны, но, например, его замена числом 12000 на IBM PC делает это необходимым, поскольку тогда выражение 3*LIMIT будет равно 36000, что превышает максимальное значение величины типа int в этой системе).
Если в вашей вычислительной системе отсутствует громкоговоритель или звонок, вы могли бы заменить оператор putchar(‘\007’) на printf («Стук копыт»). Эта программа произведет впечатление на ваших друзей и, возможно, успокоит тех, кто боится компьютеров. Мы думаем, такая программа может составить ядро какого-нибудь «Си-вычислителя», но оставляем развитие этой идеи нашим читателям.
Что делает функция getchar() вводит в программу символ, поступающий с клавиатуры терминала.
Что делает функция putchar(ch) отображает символ, содержащийся в переменной ch, на экран.
Что символы != означают: не равно.
Что такое EOF: специальный символ, указывающий на конец файла.
Как переключить стандартный ввод на ввод из файла:
Как переключить стандартный вывод на вывод в файл:
Что такое порты: средства доступа к подсоединенным устройствам.
Как использовать порты: путем вызова функций inp( ) и outp( ).
1. Выражение putchar(getchar( )) является правильным. Будет ли правильным вы ражение getchar(putchar( ))?
2. Что произойдет в результате выполнения каждого из следующих операторов?
a. putchar(‘H’ );
б. putchar(‘ \007’);
в. putchar(‘\n’);
г. putchar(‘ \b’)
3. Допустим, у вас есть программа count, подсчитывающая число символов в файле. Напишите команду, в результате выполнения которой будет пpoизвeдeн подсчет числа символов в файле essay, а результат будет помещен в файл essayct.
4. Даны программа и файлы, описанные в вопросе 3. Какие из приведенных ниже команд правильны?
a. essayct count
5. Что делает оператор outp(212, 23)?
1. Нет. У функции getchar( ) аргумент должен отсутствовать, а у функции putchar( ) аргумент обязательно должен быть.
2.
а. печать буквы Н
б. вывод символа ‘\007’, в результате чего сработает громкоговоритель
в. переход на новую строку на устройстве вывода
г. шаг назад на одну позицию.











