Керниган и Ритчи - Язык программирования Си (793773), страница 40
Текст из файла (страница 40)
Функция ungetcВ стандартной библиотеке содержится более ограниченная версия функции ungetch по сравнению с той,которую мы написали в главе 4. Называется она ungetc. Эта функция, имеющая прототипint ungetc(int с, FILE *fp)отправляет символ с назад в файл fp и возвращает с, а в случае ошибки EOF. Для каждого файлагарантирован возврат не более одного символа. Функцию ungetc можно использовать совместно с любой изфункций ввода вроде scanf, getc, getchar и т. д.7.8.4. Исполнение команд операционной системыФункция system(char *s) выполняет команду системы, содержащуюся в строке s, и затем возвращаетсяк выполнению текущей программы.
Содержимое s, строго говоря, зависит от конкретной операционнойсистемы. Рассмотрим простой пример: в системе UNIX инструкцияsystem("date");вызовет программу date, которая направит дату и время в стандартный вывод. Функция возвращаетзависящий от системы статус выполненной команды. В системе UNIX возвращаемый статус — это значение,переданное функцией exit.7.8.5. Управление памятьюФункции malloc и calloc динамически запрашивают блоки свободной памяти. Функция mallocvoid *malloc(size_t n)возвращает указатель на n байт неинициализированной памяти или NULL, если запрос удовлетворить нельзя.Функция callocvoid *calloc(size_t n, size_t size)возвращает указатель на область, достаточную для хранения массива из n объектов указанного размера(size), или NULL, если запрос не удается удовлетворить.
Выделенная память обнуляется.Указатель, возвращаемый функциями malloc и calloc, будет выдан с учетом выравнивания,выполненного согласно указанному типу объекта. Тем не менее, к нему должна быть применена операцияприведения к соответствующему типу12, как это сделано в следующем фрагменте программы:int *ip;ip = (int *) calloc(n, sizeofF(int));Функция free(p) освобождает область памяти, на которую указывает р, — указатель, первоначальнополученный с помощью malloc или calloc.
Никаких ограничений на порядок, в котором будетосвобождаться память, нет, но считается ужасной ошибкой освобождение тех областей, которые не былиполучены с помощью calloc или malloc.Нельзя также использовать те области памяти, которые уже освобождены. Следующий примердемонстрирует типичную ошибку в цикле, освобождающем элементы списка.for (р = head; р != NULL; р = p->next) /* НЕВЕРНО */free(р);Правильным будет, если вы до освобождения сохраните то, что вам потребуется, как в следующем цикле:for (p = head; p != NULL; p = q) {q = p->next;free(p);}В параграфе 8.7 мы рассмотрим реализацию программы управления памятью вроде malloc, позволяющуюосвобождать выделенные блоки памяти в любой последовательности.7.8.6.
Математические функцииВ <math.h> описано более двадцати математических функций. Здесь же приведены наиболееупотребительные. Каждая из них имеет один или два аргумента типа double и возвращает результат такжетипа double.sin(х)12— синус х, х в радианах.Как уже отмечалось, замечание о приведении типов значений, возвращаемых функциями malloc или calloc,—неверно. — Примеч. авт.cos(x)— косинус х, х в радианах.atan2(y, х)— арктангенс y/х, y и х в радианах.ехр(х)— экспоненциальная функция еx.log(x)— натуральный (по основанию е) логарифм х (х>0).log10(x)— обычный (по основанию 10) логарифм х (х > 0).pow(x, y)— xy.sqrt(х)— корень квадратный х (х > 0).fabs(x)— абсолютное значение x.7.8.7. Генератор случайных чиселФункция rand() вычисляет последовательность псевдослучайных целых в диапазоне от нуля до значения,заданного именованной константой RAND_MAX, которая определена в <stdlib.h>.
Привести случайныечисла к значениям с плавающей точкой, большим или равным 0 и меньшим 1, можно по формуле#define frand() ((double) rand() / (RAND_MAX+1.0))(Если в вашей библиотеке уже есть функция для получения случайных чисел с плавающей точкой, вполневозможно, что ее статистические характеристики лучше указанной.)Функция srand(unsigned) устанавливает семя для rand.
Реализации rand и srand, предлагаемыестандартом и, следовательно, переносимые на различные машины, рассмотрены в параграфе 2.7.Упражнение 7.9. Реализуя функции вроде isupper, можно экономить либо память, либо время. Напишитеоба варианта функции.8. Интерфейс с системой UNIXСвои услуги операционная система UNIX предлагает в виде набора системных вызовов, которые фактическиявляются ее внутренними функциями и к которым можно обращаться из программ пользователя. Внастоящей главе описано, как в Си-программах можно применять некоторые наиболее важные вызовы.
Есливы работаете в системе UNIX, то эти сведения будут вам полезны непосредственно и позволят повыситьэффективность работы или получить доступ к тем возможностям, которых нет в библиотеке. Даже если выиспользуете Си в другой операционной системе, изучение рассмотренных здесь примеров все равноприблизит вас к пониманию программирования на Си; аналогичные программы (отличающиеся лишьдеталями) вы встретите практически в любой операционной системе. Так как библиотека Си-программ,утвержденная в качестве стандарта ANSI, в основном отражает возможности системы UNIX, предлагаемыепрограммы помогут вам лучше понять и библиотеку.Глава состоит из трех основных частей, описывающих: ввод-вывод, файловую систему и организациюуправления памятью.
В первых двух частях предполагается некоторое знакомство читателя с внешнимихарактеристиками системы UNIX.В главе 7 мы рассматривали единый для всех операционных систем интерфейс ввода-вывода. В любойконкретной системе программы стандартной библиотеки пишутся с использованием средств именно этойконкретной системы.
В следующих нескольких параграфах мы опишем вызовы системы UNIX по вводу-выводуи покажем, как с их помощью можно реализовать некоторые разделы стандартной библиотеки.8.1. Дескрипторы файловВ системе UNIX любые операции ввода-вывода выполняются посредством чтения и записи файлов, посколькувсе внешние устройства, включая клавиатуру и экран, рассматриваются как объекты файловой системы. Этозначит, что все связи между программой и внешними устройствами осуществляются в рамках единогооднородного интерфейса.В самом общем случае, прежде чем читать или писать, вы должны проинформировать систему о действиях,которые вы намереваетесь выполнять в отношении файла; эта процедура называется открытием файла.Если вы собираетесь писать в файл, то, возможно, его потребуется создать заново или очистить от хранимойинформации.
Система проверяет ваши права на эти действия (файл существует? вы имеете к нему доступ?) и,если все в порядке, возвращает программе небольшое неотрицательное целое, называемое дескрипторомфайла. Всякий раз, когда осуществляется ввод-вывод, идентификация файла выполняется по его дескриптору,а не по имени. (Дескриптор файла аналогичен файловому указателю, используемому в стандартнойбиблиотеке, или хэндлу (handle) в MSDOS.) Вся информация об открытом файле хранится и обрабатываетсяоперационной системой; программа пользователя обращается к файлу только через его дескриптор.Ввод с клавиатуры и вывод на экран применяются настолько часто, что для удобства работы с нимипредусмотрены специальные соглашения.
При запуске программы командный интерпретатор (shell)открывает три файла с дескрипторами 0, 1 и 2, которые называются соответственно стандартным вводом,стандартным выводом и стандартным файлом ошибок. Если программа читает из файла 0, а пишет в файлы 1и 2 (здесь цифры — дескрипторы файлов), то она может осуществлять ввод и вывод, не заботясь об ихоткрытии.Пользователь программы имеет возможность перенаправить ввод-вывод в файл или из файла с помощьюзначков < и >, как, например, вprog <infile >outfileВ этом случае командный интерпретатор заменит стандартные установки дескрипторов 0 и 1 на именованныефайлы.
Обычно дескриптор файла 2 остается подсоединенным к экрану, чтобы на него шли сообщения обошибках. Сказанное верно и для ввода-вывода, связанного в конвейер. Во всех случаях замену файлаосуществляет командный интерпретатор, а не программа. Программа, если она ссылается на файл 0 (в случаеввода) и файлы 1 и 2 (в случае вывода), не знает, ни откуда приходит ее ввод, ни куда отправляется ее вывод.8.2. Нижний уровень ввода-вывода (read и write)Ввод-вывод основан на системных вызовах read и write, к которым Си-программа обращается с помощьюфункций с именами read и write.
Для обеих первым аргументом является дескриптор файла. Во второмаргументе указывается массив символов вашей программы, куда посылаются или откуда берутся данные.Третий аргумент — это количество пересылаемых байтов.int n_read = read(int fd, char *buf, int n);int n_written = write(int fd, char *buf, int n);Обе функции возвращают число переданных байтов. При чтении количество прочитанных байтов можетоказаться меньше числа, указанного в третьем аргументе. Нуль означает конец файла, а -1 сигнализирует окакой-то ошибке. При записи функция возвращает количество записанных байтов, и если это число несовпадает с требуемым, следует считать, что запись не произошла.За один вызов можно прочитать или записать любое число байтов. Обычно это число равно или 1, чтоозначает посимвольную передачу "без буферизации", или чему-нибудь вроде 1024 или 4096,соответствующих размеру физического блока внешнего устройства.















