RuLibrary.com

ГЛАВНАЯ | ПОИСК | ТОП | КАРТА САЙТА      

 
 


 

Керниган Ричи >> Язык C (страница 21)


STRUCT TNODE *RIGHT; /* RIGHT CHILD */

\) TREENODE, *TREEPTR;

В результате получаем два новых ключевых слова: TREENODE (структура) и TREEPTR (указатель на структуру). Тогда функ- цию TALLOC можно записать в виде

TREEPTR TALLOC()

\(

CHAR *ALLOC();

RETURN((TREEPTR) ALLOC(SIZEOF(TREENODE)));

\)

Необходимо подчеркнуть, что описание TYPEDEF не приводит к созданию нового в каком-либо смысле типа; оно только до- бавляет новое имя для некоторого существующего типа. при этом не возникает и никакой новой семантики: описанные таким способом переменные обладают точно теми же свойствами, что и переменные, описанные явным образом. По существу конструкция TYPEDEF сходна с #DEFINE за исключением того, что она интер- претируется компилятором и потому может осуществлять подста- новки текста, которые выходят за пределы возможностей мак- ропроцессора языка "C". Например,

TYPEDEF INT (*PFI) ();

создает тип PFI для "указателя функции, возвращающей значе- ние типа INT", который затем можно было бы использовать в программе сортировки из главы 5 в контексте вида

PFI STRCMP, NUMCMP, SWAP;

Имеются две основные причины применения описаний TYPEDEF. Первая причина связана с параметризацией программы, чтобы облегчить решение проблемы переносимости. Если для ти- пов данных, которые могут быть машинно-зависимыми, использо- вать описание TYPEDEF, то при переносе программы на другую машину придется изменить только эти описания. Одна из типич- ных ситуаций состоит в использовании определяемых с помощью TYPEDEF имен для различных целых величин и в последующем подходящем выборе типов SHORT, INT и LONG для каждой имею- щейся машины. Второе назначение TYPEDEF состоит в обеспечении лучшей доку- ментации для программы - тип с именем TREEPTR может оказать- ся более удобным для восприятия, чем тип, который описан только как указатель сложной структуры. И наконец, всегда существует вероятность, что в будущем ком- пилятор или некоторая другая программа, такая как LINT, смо- жет использовать содержащуюся в описаниях TYPEDEF информацию для проведения некоторой дополнительной проверки программы.

* 7. Ввод и вывод *

Средства ввода/вывода не являются составной частью языка "с", так что мы не выделяли их в нашем предыдущем изложении. Однако реальные программы взаимодействуют со своей окружаю- щей средой гораздо более сложным образом, чем мы видели до сих пор. В этой главе будет описана "стандартная библиотека ввода/вывода", то есть набор функций, разработанных для обеспечения стандартной системы ввода/вывода для "с"- прог- рамм. Эти функции предназначены для удобства программного интерфейса, и все же отражают только те операции, которые могут быть обеспечены на большинстве современных операцион- ных систем. Процедуры достаточно эффективны для того, чтобы пользователи редко чувствовали необходимость обойти их "ради эффективности", как бы ни была важна конкретная задача. И, наконец, эти процедуры задуманы быть "переносимыми" в том смысле, что они должны существовать в совместимом виде на любой системе, где имеется язык "с", и что программы, кото- рые ограничивают свои взаимодействия с системой возможностя- ми, предоставляемыми стандартной библиотекой, можно будет переносить с одной системы на другую по существу без измене- ний.

Мы здесь не будем пытаться описать всю библиотеку вво- да/вывода; мы более заинтересованы в том, чтобы продемонст- рировать сущность написания "с"-программ, которые взаимодей- ствуют со своей операционной средой.

7.1. Обращение к стандартной библиотеке

Каждый исходный файл, который обращается к функции из стандартной библиотеки, должен вблизи начала содержать стро- ку

#INCLUDE <STDIO.H>

в файле STDIO.H определяются некоторые макросы и переменные, используемые библиотекой ввода/вывода. Использование угловых скобок вместо обычных двойных кавычек - указание компилятору искать этот файл в справочнике, содержащем заголовки стан- дартной информации (на системе UNIX обычно LUSRLINELUDE).

Кроме того, при загрузке программы может оказаться необ- ходимым указать библиотеку явно; на системе PDP-11 UNIX, например, команда компиляции программы имела бы вид:

CC исходные файлы и т.д. -LS

где -LS указывает на загрузку из стандартной библиотеки.

7.2. Стандартный ввод и вывод - функции GETCHAR и PUTCHAR

Самый простой механизм ввода заключается в чтении по од- ному символу за раз из "стандартного ввода", обычно с терми- нала пользователя, с помощью функции GETCHAR. Функция GETCHAR() при каждом к ней обращении возвращает следующий

вводимый символ. В большинстве сред, которые поддерживают язык "с", терминал может быть заменен некоторым файлом с по- мощью обозначения < : если некоторая программа PROG исполь- зует функцию GETCHAR то командная строка

PROG<INFILE

приведет к тому, что PROG будет читать из файла INFILE, а не с терминала. Переключение ввода делается таким образом, что сама программа PROG не замечает изменения; в частности стро- ка"<INFILE" не включается в командную строку аргументов в ARGV. Переключение ввода оказывается незаметным и в том слу- чае, когда вывод поступает из другой программы посредством поточного (PIPE) механизма; командная строка

OTHERPROG \! PROG

прогоняет две программы, OTHERPROG и PROG, и организует так, что стандартным вводом для PROG служит стандартный вывод OTHERPROG.

Функция GETCHAR возвращает значение EOF, когда она попа- дает на конец файла, какой бы ввод она при этом не считыва- ла. Стандартная библиотека полагает символическую константу EOF равной -1 (посредством #DEFINE в файле STDIO.H), но про- верки следует писать в терминах EOF, а не -1, чтобы избежать зависимости от конкретного значения.

Вывод можно осуществлять с помощью функции PUTCHAR(C), помещающей символ 'с' в "стандартный ввод", который по умол- чанию является терминалом. Вывод можно направить в некоторый файл с помощью обозначения > : если PROG использует PUTCHAR, то командная строка

PROG>OUTFILE

приведет к записи стандартного вывода в файл OUTFILE, а не на терминал. На системе UNIX можно также использовать поточ- ный механизм. Строка

PROG \! ANOTHERPROG

помещает стандартный вывод PROG в стандартный ввод ANOTHERPROG. И опять PROG не будет осведомлена об изменении направления.

Вывод, осуществляемый функцией PRINTF, также поступает в стандартный вывод, и обращения к PUTCHAR и PRINTF могут пе- ремежаться.

Поразительное количество программ читает только из одно- го входного потока и пишет только в один выходной поток; для таких программ ввод и вывод с помощью функций GETCHAR, PUTCHAR и PRINTF может оказаться вполне адекватным и для на- чала определенно достаточным. Это особенно справедливо тог-

да, когда имеется возможность указания файлов для ввода и вывода и поточный механизм для связи вывода одной программы с вводом другой. Рассмотрим, например, программу LOWER, ко- торая преобразует прописные буквы из своего ввода в строч- ные:

#INCLUDE <STDIO.H>

MAIN() /* CONVERT INPUT TO LOWER CASE */ \(

INT C;

WHILE ((C = GETCHAR()) != EOF)

PUTCHAR(ISUPPER(C) ? TOLOWER(C) : C); \)

"Функции" ISUPPER и TOLOWER на самом деле являются макроса- ми, определенными в STDIO.H . Макрос ISUPPER проверяет, яв- ляется ли его аргумент буквой из верхнего регистра, и возв- ращает ненулевое значение, если это так, и нуль в противном случае. Макрос TOLOWER преобразует букву из верхнего регист- ра в ту же букву нижнего регистра. Независимо от того, как эти функции реализованы на конкретной машине, их внешнее по- ведение совершенно одинаково, так что использующие их прог- раммы избавлены от знания символьного набора.

Если требуется преобразовать несколько файлов, то можно собрать эти файлы с помощью программы, подобной утилите CAT системы UNIX,

CAT FILE1 FILE2 ... \! LOWER>OUTPUT

и избежать тем самым вопроса о том, как обратиться к этим файлам из программы. (Программа CAT приводится позже в этой главе).

Кроме того отметим, что в стандартной библиотеке вво- да/вывода "функции" GETCHAR и PUTCHAR на самом деле могут быть макросами. Это позволяет избежать накладных расходов на обращение к функции для обработки каждого символа. В главе 8 мы продемонстрируем, как это делается.

7.3. Форматный вывод - функция PRINTF

Две функции: PRINTF для вывода и SCANF для ввода (следу- ющий раздел) позволяют преобразовывать численные величины в символьное представлEние и обратно. Они также позволяют ге- нерировать и интерпретировать форматные строки. Мы уже всюду в предыдущих главах неформально использовали функцию PRINTF; здесь приводится более полное и точное описание. Функция

PRINTF(CONTROL, ARG1, ARG2, ...)

преобразует, определяет формат и печатает свои аргументы в стандартный вывод под управлением строки CONTROL. Управляю- щая строка содержит два типа объектов: обычные символы, ко- торые просто копируются в выходной поток, и спецификации преобразований, каждая из которых вызывает преобразование и печать очередного аргумента PRINTF.

Каждая спецификация преобразования начинается с символа % и заканчивается символом преобразования. Между % и симво- лом преобразования могут находиться: - знак минус, который указывает о выравнивании преобразован-

ного аргумента по левому краю его поля. - Строка цифр, задающая минимальную ширину поля. Преобразо-

ванное число будет напечатано в поле по крайней мере этой

ширины, а если необходимо, то и в более широком. Если пре-

образованный аргумент имеет меньше символов, чем указанная

ширина поля, то он будет дополнен слева (или справа, если

было указано выравнивание по левому краю)заполняющими сим-

волами до этой ширины. Заполняющим символом обычно являет-

ся пробел, а если ширина поля указывается с лидирующим ну-

лем, то этим символом будет нуль (лидирующий нуль в данном

случае не означает восьмеричной ширины поля). - Точка, которая отделяет ширину поля от следующей строки

цифр. - Строка цифр (точность), которая указывает максимальное

число символов строки, которые должны быть напечатаны, или

число печатаемых справа от десятичной точки цифр для пере-

менных типа FLOAT или DOUBLE. - Модификатор длины L, который указывает, что соответствую-

щий элемент данных имеет тип LONG, а не INT.

Ниже приводятся символы преобразования и их смысл:

D - аргумент преобразуется к десятичному виду. O - Аргумент преобразуется в беззнаковую восьмеричную форму

(без лидирующего нуля). X - Аргумент преобразуется в беззнаковую шестнадцатеричную

форму (без лидирующих 0X). U - Аргумент преобразуется в беззнаковую десятичную форму. C - Аргумент рассматривается как отдельный символ. S - Аргумент является строкой: символы строки печатаются до

тех пор, пока не будет достигнут нулевой символ или не бу-

дет напечатано количество символов, указанное в специфика-

ции точности. E - Аргумент, рассматриваемый как переменная типа FLOAT или

DOUBLE, преобразуется в десятичную форму в виде

[-]M.NNNNNNE[+-]XX, где длина строки из N определяется

указанной точностью. Точность по умолчанию равна 6. F - Аргумент, рассматриваемый как переменная типа FLOAT или

DOUBLE, преобразуется в десятичную форму в виде

[-]MMM.NNNNN, где длина строки из N определяется указанной

точностью. Точность по умолчанию равна 6. отметим, что эта

точность не определяет количество печатаемых в формате F

значащих цифр.

G - Используется или формат %E или %F, какой короче; незна-

чащие нули не печатаются. Если идущий за % символ не является символом преобразования, то печатается сам этот символ; следовательно,символ % можно напечатать, указав %%.

Большинство из форматных преобразований очевидно и было проиллюстрировано в предыдущих главах. Единственным исключе- нием является то, как точность взаимодействует со строками. Следующая таблица демонстрирует влияние задания различных спецификаций на печать "HELLO, WORLD" (12 символов). Мы по- местили двоеточия вокруг каждого поля для того, чтобы вы могли видеть его протяженность.

:%10S: :HELLO, WORLD: :%10-S: :HELLO, WORLD: :%20S: : HELLO, WORLD: :%-20S: :HELLO, WORLD : :%20.10S: : HELLO, WOR: :%-20.10S: :HELLO, WOR : :%.10S: :HELLO, WOR:

Предостережение: PRINTF использует свой первый аргумент для определения числа последующих аргументов и их типов. Ес- ли количество аргументов окажется недостаточным или они бу- дут иметь несоответственные типы, то возникнет путаница и вы получите бессмысленные результаты.

Упражнение 7-1

Напишите программу, которая будет печатать разумным об- разом произвольный ввод. Как минимум она должна печатать неграфические символы в восьмеричном или шестнадцатеричном виде (в соответствии с принятыми у вас обычаями) и склады- вать длинные строки.

7.4. Форматный ввод - функция SCANF

Осуществляющая ввод функция SCANF является аналогом PRINTF и позволяет проводить в обратном направлении многие из тех же самых преобразований. Функция

SCANF(CONTROL, ARG1, ARG2, ...)

читает символы из стандартного ввода, интерпретирует их в соответствии с форматом, указанном в аргументе CONTROL, и помещает результаты в остальные аргументы. Управляющий аргу- мент описывается ниже; другие аргументы, каждый из которых должен быть указателем, определяют, куда следует поместить соответствующим образом преобразованный ввод.

Управляющая строка обычно содержит спецификации преобра- зования, которые используются для непосредственной интерпре- тации входных последовательностей. Управляющая строка может содержать: - пробелы, табуляции или символы новой строки ("символы пус-

тых промежутков"), которые игнорируются.

- Обычные символы (не %), которые предполагаются совпадающи-

ми со следующими отличными от символов пустых промежутков

символами входного потока. - Спецификации преобразования, состоящие из символа %, нео-

бязательного символа подавления присваивания *, необяза-

тельного числа, задающего максимальную ширину поля и сим-

Название книги: Язык C
Автор: Керниган Ричи
Просмотрено 45106 раз

123456789101112131415161718192021222324252627282930313233


 
Page generation 0.003 seconds