Pers.narod.ru. Обучение. Лекции по Си. Глава 8 |
Основные отличия стандартной библиотеки Си от других языков состоят в следующем:
· более сильная интеграция с языком. Так, в самом языке Си нет никаких средств ввода‑вывода, поэтому ни одна программа не может быть написана без использования функций стандартной библиотеки;
· несмотря на то, что существуют стандарты языка Си, ряд функций уникален для той или иной системы программирования;
· многие функции имеют несколько "подфункций", решающих разные задачи и обычно отличающихся одной буквой в названии, например, abs, fabs и labs для функции взятия модуля, printf, fprintf и sprintf для стандартной функции печати и т.д.
Далее рассматриваются основные разделы и функции стандартной библиотеки. Более полная информация может быть получена из рекомендуемых книг и справочной системы.
8.1. Определение класса символов и преобразование символов
Все функции, приведенные в табл. 8.1 имеют тип int и возвращают int. Возвращаемая величина равна 0, если условие проверки не выполняется. Все функции реализованы как макроопределения, заданные в файле ctype.h.
Таблица 8.1. Функции проверки и преобразования символов
Функция |
Краткое описание |
isalnum |
проверка на латинскую букву или цифру |
isalpha |
проверка на латинскую букву |
isascii |
проверка на символ из набора кодировки ASCII |
iscntrl |
проверка на управляющий символ |
isdigit |
проверка на десятичную цифру |
isgraph |
проверка на печатный символ, исключая пробел |
islower |
проверка на малую латинскую букву |
isprint |
проверка на печатный символ |
ispunct |
проверка на знак пунктуации |
isspace |
проверка на пробельный символ |
isupper |
проверка на заглавную латинскую букву |
isxdigit |
проверка на шестнадцатеричную цифру |
toascii |
преобразование символа в код ASCII |
tolower |
проверка и преобразование в малую латинскую букву, если передана заглавная буква |
toupper |
проверка и преобразование малой латинскую буквы в заглавную |
_tolower |
преобразование латинскую буквы в малую (без проверки) |
_toupper |
преобразование латинскую буквы в заглавную (без проверки) |
В примере ниже символ c преобразуется к верхнему регистру:
char c='x';
if (tolower(c)) c=toupper (c);
Пример ниже подсчитывает относительные частоты букв кириллицы во вводимом тексте.
#include<stdio.h>
#include<conio.h>
void main(void) {
char lower_case[] =
"абвгдежзийклмнопрстуфхцчшщьъыэюя";
char upper_case[] =
"АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЪЫЭЮЯ";
static float frequency[sizeof(lower_case)],
total=0.;
char ch; int index;
/* Завершение ввода - символ '$' */
puts("Печатайте текст\n");
while((ch = getchar()) != '$') {
//Определяем порядковый номер буквы.
for (index = 0; index < 32; index++)
if((lower_case[index]==ch)||
(upper_case[index]==ch)) {
total++;
frequency[index]++;
break;
}
}
puts("\nОтносительные частоты\n");
if (total) {
for (index = 0; index < 32; index++) {
frequency[index]=
frequency[index]*100./total;
printf("%c-%5.1f ",upper_case[index],
frequency[index]);
}
}
else
puts("\aНи одна буква не введена");
}}
8.2. Работа с областями памяти и строками
Основными понятиями являются буфер и строка:
Буфер - это область памяти, рассматриваемая как последовательность байтов. Размер буфера всегда задается явно.
Строка - это последовательность байт, завершаемая байтом с кодом '\0'. В языке Си длина строки, в отличие от буфера, нигде не хранится и может быть получена в цикле сканирования строки слева направо. Нулевой байт также считается принадлежащим строке, поэтому строка из n символов требует (n+1)*sizeof(char) байт памяти.
В библиотеке mem.h для работы с буферами предназначен ряд функций, основные из них перечислены в табл. 8.2.
Таблица 8.2. Функции для работы с буферами
Функция |
Краткое описание |
memccpy |
void *memcpy (void *s1, const void *s2, int c , size_t n); копирует n символов из буфера s2 в s1 с указанием дополнительного стоп‑символа c |
memchr |
void *memchr (const void *s, int c, size_t n); возвращает указатель на первое вхождение символа c в буфер s длиной n символов |
memcmp |
int memcmp(const void *s1, const void *s2, size_t n); сравнивает n символов из двух буферов как беззнаковые. memicmp - то же с игнорированием регистра латинских символов |
memcpy |
void *memcpy(void *dest, const void *src, size_t n); копирует n символов из буфера src в dest |
memset |
void *memset(void *s, int c, size_t n); инициализирует значением c указанные n байт в буфере s |
memmove |
void *memmove(void *dest, const void *src, size_t n); работает как memcpy, но корректно обрабатывает перекрывающиеся области памяти |
В компиляторах от Borland прототипы указанных функций содержит также библиотека string.h.
Прототипы функций работы со строками содержатся в файле string.h. Все функции работают со строками, завершающимися нулевым байтом '\0'. Для функций, возвращающих указатели, в случае ошибки возвращается NULL.
Для работы со строками существуют библиотечные функции, основные из которых описаны в табл. 8.3.
Типизированный модификатор size_t определен в ряде стандартных библиотек следующим образом:
typedef unsigned size_t;
Таблица 8.3. Функции для работы со строками
Функция |
Краткое описание |
strcat |
char *strcat(char *dest, const char *src); конкатенация (склеивание) строки src с dest |
strncat |
char *strncat(char *dest, const char *src, size_t maxlen); добавить не более n символов из src в dest |
strchr |
char *strchr(const char *s, int c); найти первое вхождение символа с в строку s и вернуть указатель на найденное |
strrchr |
char *strrchr(const char *s, int c); найти последнее вхождение символа c в строку s и вернуть указатель на найденное |
strcmp |
int strcmp(const char *s1, const char *s2); сравнить две строки. Возвращаемое значение меньше 0, если s1 лексикографически (алфавитно) предшествует s2, ноль, если s1=s2, больше нуля, если s1 больше s2 |
strncmp |
int strncmp(const char *s1, const char *s2, size_t n); сравнить две строки, учитывая не более n символов |
stricmp |
int stricmp(const char *s1, const char *s2); сравнить две строки, считая латинские символы нижнего и верхнего регистров эквивалентными |
strnicmp |
int strnicmp(const char *s1, const char *s2, size_t n); то же, что stricmp и strncmp совместно |
strcpy |
char *strcpy(char *dest, const char *src); копировать строку src в dest, включая завершающий нулевой байт |
strncpy |
char *strncpy(char *dest, const char *src, size_t n); копировать не более n символов из src в dest |
strdup |
char *strdup(const char *s); дублирование строки с выделением памяти, например char *dup_str, *string = "abcde"; dup_str = strdup(string); |
strlen |
size_t strlen(const char *s); возвращает длину строки в символах |
strlwr |
char *strlwr(char *s); преобразовать строку в нижний регистр (строчные буквы) |
strupr |
char *strupr(char *s); преобразовать строку в верхний регистр (заглавные буквы) |
strrev |
char *strrev(char *s); инвертировать (перевернуть) строку |
strnset |
char *strnset(char *s, int ch, size_t n); установить n символов строки s в заданное значение ch |
strset |
char *strset(char *s, int ch); установить все символы строки s в заданное значение ch |
strspn |
size_t strspn(const char *s1, const char *s2); ищет начальный сегмент s1, целиком состоящий из символов s2. Вернет номер позиции, с которой строки различаются, например: char *string1 = "1234567890"; char *string2 = "abc123"; int l = strspn(string1, string2); printf("Position=%d\n", l); //l=3 |
strсspn |
size_t strspn(const char *s1, const char *s2); ищет начальный сегмент s1, целиком состоящий из символов, не входящих в s2. Вернет номер позиции, с которой строки различаются, например char *string1 = "123abc7890"; char *string2 = "abc"; int l = strcspn(string1, string2); printf("Position=%d\n", l); //l=3 |
strstr |
char *strstr(const char *s1, const char *s2); найти первую подстановку строки s2 в s1 и вернуть указатель на найденное |
strtok |
char *strtok(char *s1, const char *s2); найти следующий разделитель из набора s2 в строке s1 |
strerror |
char *strerror(int errnum); сформировать в строке сообщение об ошибке, состоящее из двух частей: системной диагностики и необязательного добавочного пользовательского сообщения |
Пример иллюстрирует действие функции strtok, позволяющей разбивать строку на лексемы:
char input[16] = "abc,d";
char *p;
/* strtok помещает нулевой байт вместо
разделителя лексем, если поиск был успешен */
p = strtok(input, ",");
if (p) printf("%s\n", p); //"abc"
/* второй вызов использует NULL как
первый параметр и возвращает
указатель на следующую лексему */
p = strtok(NULL, ",");
if (p) printf("%s\n", p); //"d"
Разумеется, число выделяемых лексем и набор разделителей могут быть любыми. В коде, приведенном ниже, лексемы могут разделяться пробелом, запятой или горизонтальной табуляцией, а прием и синтаксический разбор строк завершается при вводе пустой строки.
#include <string.h>
#include <stdio.h>
#define BLANK_STRING ""
void main(void){
char *token, buf[81],*separators = "\t,. ";
int i;
puts("Вводите строки\n\
Завершение - нажатие ENTER.\n");
while(strcmp(gets(buf),BLANK_STRING)!=0) {
i = 0;
token = strtok(buf, separators);
while(token != NULL) {
printf("Лексема %d - %s\n", i, token);
token = strtok(NULL, separators);
i++;
}
}}
Следующий пример показывает простейшее использование обработчика ошибок strerror:
#include <stdio.h>
#include <errno.h>
:
char *buffer;
buffer = strerror(errno);
printf("Error: %s\n", buffer);
8.3. Функции преобразования типов
Описанные в табл. 8.4 функции объявлены в стандартной библиотеке stdlib.h. Прототип функции atof содержится, кроме того, в файле math.h.
Таблица 8.4. Функции преобразования типов
Функция |
Краткое описание |
atof |
double atof(const char *s); преобразование строки, в представляемое ей число типа double. На переполнение возвращает плюс или минус HUGE_VAL (константа из библиотеки) |
atoi |
int atoi(const char *s); преобразование строки в число типа int (целое). При неудачном преобразовании вернет 0 |
atol |
long atol(const char *s); преобразование строки в число типа long (длинное целое) |
ecvt |
char *ecvt(double value, int ndig, int *dec, int *sign); преобразование числа типа double в строку. ndig - требуемая длина строки, dec возвращает положение десятичной точки от 1-й цифры числа, sign возвращает знак |
fcvt |
char *fcvt(double value, int ndig, int *dec, int *sign); преобразование числа типа double в строку. В отличие от ecvt, dec возвращает количество цифр после десятичной точки. Если это количество превышает ndig, происходит округление до ndig знаков. |
gcvt |
char *gcvt(double value, int ndig, char *buf); преобразование числа типа double в строку buf. Параметр ndig определяет требуемое число цифр в записи числа. |
itoa |
char *itoa (int value, char *string, int radix); преобразование числа типа int в строку, записанную в системе счисления с основанием radix (от 2 до 36 включительно) |
ltoa |
char *ltoa (long value, char *string, int radix); преобразование числа типа long в строку |
ultoa |
char *ultoa (unsigned long value, char *string, int radix); преобразование числа типа unsigned long в строку |
Некоторые системы программирования предоставляет также функции, перечисленные в табл. 8.5.
Таблица 8.5. Дополнительные функции преобразования типов
Функция |
Краткое описание |
strtod |
преобразование строки в число типа double (покрывает возможности atof) |
strtol |
преобразование строки в число типа long (покрывает возможности atol) |
strtoul |
преобразование строки в число типа unsigned long |
В приведенном ниже коде число n преобразуется в строку s, представляющую собой запись числа в системе счисления с основанием r.
int n;
printf ("\nN="); scanf ("%d", &n);
char s[25];
int r=10;
do {
printf ("\nRadix[2-36]: ");
fflush (stdin); scanf ("%d",&r);
} while (r<2 || r>36);
s=itoa (n,s,r);
printf ("\n%d in %d notation is %s",n,r,s);
8.4. Математические функции
Прототипы математических функций содержатся в файле math.h, за исключением прототипов _clear87, _control87, _fpreset, status87, определенных в файле float.h.
Вещественные функции, как правило, работают с двойной точностью (тип double).
Многие функции имеют версии, работающие с учетверенной точностью (тип long double). Имена таких функций имеют суффикс "l" в конце (atan и atanl, fmod и fmodl и т. д.). Действие модификатора long в применении к double зависит от архитектуры ЭВМ.
Таблица 8.6. Математические функции
Функция |
Краткое описание |
abs |
нахождение абсолютного значения выражения типа int |
acos |
вычисление арккосинуса. Аргументы этой и других тригонометрических функций задаются в радианах |
asin |
вычисление арксинуса |
atan |
вычисление арктангенса х |
atan2 |
вычисление арктангенса от у/х |
cabs |
нахождение абсолютного значения комплексного числа |
ceil |
нахождение наименьшего целого, большего или равного х |
_clear87 |
получение значения и инициализация слова состояния сопроцессора и библиотеки арифметики с плавающей точкой |
_control87 |
получение старого значения слова состояния для функций арифметики с плавающей точкой и установка нового состояния |
cos |
вычисление косинуса |
cosh |
вычисление гиперболического косинуса |
exp |
вычисление экспоненты |
fabs |
нахождение абсолютного значения типа double |
floor |
нахождение наибольшего целого, меньшего или равного х |
fmod |
нахождение остатка от деления х/у |
_fpreset |
повторная инициализация пакета плавающей арифметики |
frexp |
вычисляет для х вещественную мантиссу m и целое n так, что x=m*2n |
hypot |
вычисление гипотенузы |
labs |
нахождение абсолютного значения типа long |
ldexp |
вычисление х*2e |
log |
вычисление натурального логарифма |
log10 |
вычисление логарифма по основанию 10 |
matherr |
управление реакцией на ошибки при выполнении функций математической библиотеки |
modf |
разложение х на дробную и целую часть |
pow |
вычисление х в степени у |
sin |
вычисление синуса |
sinh |
вычисление гиперболического синуса |
sqrt |
нахождение квадратного корня |
_status87 |
получение значения слова состояния с плавающей точкой |
tan |
вычисление тангенса |
tanh |
вычисление гиперболического тангенса |
В библиотеке определен также ряд констант, таких как M_PI (число π), M_E (основание натурального логарифма e) и др.
Функция matherr, которую пользователь может определить в своей программе, вызывается любой библиотечной математической функцией при возникновении ошибки. Эта функция определена в библиотеке, но может быть переопределена для установки различных процедур обработки ошибок.
int matherr (struct exception *a) {
if (a->type == DOMAIN)
if (!strcmp(a->name,"sqrt")) {
a->retval = sqrt (-(a->arg1));
return 1;
}
return 0;
}
double x = -2.0, y;
y = sqrt(x);
printf("Matherr corrected value: %lf\n",y);
8.5. Динамическое распределение памяти
Стандартная библиотека предоставляет механизм распределения динамической памяти (heap). Этот механизм позволяет динамически запрашивать из программы дополнительные области оперативной памяти.
Работа функций динамического распределения памяти разл1ичается для различных моделей памяти, поддерживаемых системой программирования (см. п. 11).
В малых моделях памяти (tiny, small, medium) доступно для использования все пространство между концом сегмента статических данных программы и вершиной программного стека, за исключением 256‑байтной буферной зоны непосредственно около вершины стека.
В больших моделях памяти (compact, large, huge) все пространство между стеком программы и верхней границей физической памяти доступно для динамического размещения памяти.
Как правило, функция выделения памяти возвращает нетипизированный указатель на начало выделенной области памяти. Для динамического распределения памяти используются функции, описанные в табл. 8.7. Их прототипы содержатся в файлах alloc.h и stdlib.h.
Таблица 8.7. Функции динамического распределения памяти
Функция |
Краткое описание |
calloc |
void *calloc(size_t nitems, size_t size); выделяет память под nitems элементов по size байт и инициализирует ее нулями |
malloc |
void *malloc(size_t size); выделяет память объемом size байт |
realloc |
void *realloc (void *block, size_t size); пытается переразместить ранее выделенный блок памяти, изменив его размер на size |
free |
void free(void *block); пытается освободить блок, полученный посредством функции calloc, malloc или realloc |
Приведем примеры использования функций из табл. 8.7.
char *str = NULL;
str = (char *) calloc(10, sizeof(char));
/* . . . */
char *str;
if ((str = (char *) malloc(10)) == NULL) {
printf(
"Not enough memory to allocate buffer\n");
exit(1); }
/* . . . */
char *str;
str = (char *) malloc(10);
/* . . . */
printf("String is %s\nAddress is %p\n",
str, str);
str = (char *) realloc(str, 20);
printf("String is %s\nAddress is %p\n",
str, str);
free (str);
Функции calloc и malloc выделяют блоки памяти, функция malloc при этом выделяет заданное число байт, тогда как calloc выделяет и инициализирует нулями массив элементов заданного размера.
Ряд систем программирования предоставляют множество альтернативных функций, не входящих в ядро языка Си. Они кратко описаны в табл. 8.8.
Таблица 8.8. Другие функции распределения памяти
Функция |
Краткое описание |
alloca |
выделение блока памяти из программного стека |
_expand |
изменение размера блока памяти, не меняя местоположения блока |
_ffree |
освобождение блока, выделенного посредством функции _fmalloc |
_fmalloc |
выделение блока памяти вне данного сегмента |
_freect |
определить примерное число областей заданного размера, которые можно выделить |
_fmsize |
возвращает размер блока памяти, на который указывает дальний (far) указатель |
halloc |
выделить память для большого массива (объемом более 64 Кб) |
hfree |
освободить блок памяти, выделенный посредством функции halloc |
_memavl |
определить примерный размер в байтах памяти, доступной для выделения |
_msize |
определить размер блока, выделенного посредством функций calloc, malloc, realloc |
_nfree |
освобождает блок, выделенный посредством _nmalloc |
_nmalloc |
выделить блок памяти в заданном сегменте |
_nmsize |
определить размер блока, на который указывает близкий (near) указатель |
stackavail |
определить объем памяти, доступной для выделения посредством функции alloca |
brk |
переустановить адрес первого байта оперативной памяти, недоступного программе (начала области памяти вне досягаемости программы) |
allocmem |
низкоуровневая функция выделения памяти |
freemem |
низкоуровневая функция возврата памяти операционной системе |
coreleft |
узнать, сколько осталось памяти для выделения в данном сегменте |
farcalloc |
выделить блок памяти вне данного сегмента |
farcoreleft |
определить, сколько памяти для размещения осталось вне данного сегмента |
farmalloc |
выделить блок памяти вне данного сегмента |
farrealloc |
изменить размер блока, ранее выделенного функцией farmalloc или farcalloc |
farfree |
освободить блок, ранее выделенный функцией farmalloc или farcalloc |
Эти функции могут быть полезны для выделения памяти под данные объемом более сегмента (64 Кб), а также для определения объема свободной памяти.
Приведем более подробный пример, иллюстрирующий динамическое выделение памяти для вектора и последующую работу с этим вектором.
#include <stdio.h>
#include <alloc.h>
#include <stdlib.h>
void wait (void) {
fflush (stdin); getchar(); }
void error (int n) {
switch (n) {
case 0: printf ("\n OK"); break;
case 1: printf ("\n Bad size of array!");
break;
case 2: printf
("\n Can't allocate memory!"); break;
}
wait(); exit (n);
}
int *allocate (long n) {
return (int *)malloc(n*sizeof(int));
}
void main(void) {
long n=0;
printf ("\n Enter number of items:");
scanf ("%ld",&n);
if (n<2) error (1);
int *p=allocate(n);
if (p==NULL) error (2);
for (long i=0; i<n; i++) {
*(p+i)=random(n);
printf ("%6ld",*(p+i));
}
wait ();
free (p);
}
При работе с динамической матрицей следует сначала выделить память под массив указателей на строки, а затем каждый из этих указателей связать с динамически выделенной областью, куда запишутся элементы строк. Приведем только измененные фрагменты предыдущего примера.
int **allocatematrix (long n, long m) {
//выделяет память для матрицы n*m
int **p=NULL;
p=(int **)malloc(n*sizeof(int *));
if (p==NULL) error (2);
for (long i=0; i<n; i++) {
p[i]=(int *)malloc(m*sizeof(int));
if (p[i]==NULL) error (2);
}
return p;
}
/* . . . */
int **p=allocatematrix(n,m);
for (long i=0; i<n; i++) {
printf ("\n ");
for (long j=0; j<m; j++) {
p[i][j]=random(n*m);
printf ("%6ld",p[i][j]);
}
}
Правильный порядок освобождения занятой памяти для динамической матрицы будет таков:
for (long i=n-1; i>-1; i--) free (p[i]);
free (p);
8.6. Функции стандартного ввода и вывода
Все функции форматированного ввода и вывода используют в качестве первого параметра строку формата, управляющую способом преобразования данных. В строке формата символы после % и до первого разделителя рассматриваются как спецификация преобразования значений выводимой переменной (или просто "формат"). Количество спецификаций в строке формата должно совпадать с количеством вводимых или выводимых значений, передаваемых, начиная со второго параметра функции.
Сначала рассмотрим подробнее структуру форматной спецификации. Она имеет следующий общий вид:
%<флаги><ширина><.точность><модификатор>тип
В символы <> заключены необязательные элементы спецификации, таким образом, единственным обязательным элементом является тип. Символы управления форматированием, составляющие различные элементы спецификации, описаны в табл. 8.9.
Таблица 8.9. Символы управления форматированием
Элемент спецификации |
Действие |
флаги |
|
‑ |
выравнивание числа влево в пределах выделенного поля, справа дополняется пробелами. По умолчанию устанавливается выравнивание вправо |
+ |
выводится знак числа символом "‑" или "+" |
пробел |
перед положительным числом выводится пробел, для отрицательных всегда выводится знак "-" |
# |
для целых чисел выводится идентификатор системы счисления: 0 перед числом для вывода в 8‑ричной с.с. 0x или 0X перед числом для вывода в 16‑ричной с.с. ничего для чисел, выводимых в 10‑ной с.с. Выводится десятичная точка для чисел типа float |
ширина - воздействует только на вывод |
|
n |
целое n определяет минимальную ширину поля в n символов. Если этой ширины недостаточно, выводится столько символов, сколько есть. Незаполненные позиции дополняются пробелами. |
0n |
то же, что n, но позиции слева для целого числа дополняются нулями |
* |
следующий аргумент из списка аргументов задает ширину |
точность - воздействует только на вывод |
|
ничего |
точность по умолчанию |
.0 |
для d,i,o,u,x точность по умолчанию. Для e,E,f десятичная точка отсутствует |
.n |
Для e,E,f не более n знаков после точки |
* |
следующий аргумент из списка аргументов задает точность |
модификатор - действует там, где применим |
|
h |
для d, i, o, u, x, X аргумент является short int |
l |
для d, i, o, u, x, X аргумент является long int для e, E, f, F, g, G аргумент является double |
тип переменной |
|
с |
Тип char При вводе читается и передается 1 байт. При выводе переменная преобразуется к типу char. В файл передается 1 байт |
d i o u x X |
Тип int десятичное целое со знаком десятичное целое со знаком 8‑ричное целое без знака 10‑ное целое без знака 16‑ричное целое без знака. При выводе использует a:f 16‑ричное целое без знака. При выводе использует A:F При вводе действие x и X не различается |
f e
E
g
G |
Тип float число со знаком в формате <->dddd.ddd число со знаком в формате <->dddd.ddde<+ или ‑>ddd число со знаком в формате <->dddd.dddE<+ или ‑>ddd При вводе e и E не различаются число со знаком в формате e или f в зависимости от указанной точности число со знаком в формате E или F в зависимости от указанной точности При вводе действие g и G не различается |
s
p |
Тип char * При вводе принимает символы без преобразования, пока не встретится разделитель '\n' или не достигнута указанная точность. В программу передаются символы до '\n' или пробела. При выводе выдает в поток все символы, пока не встретится '\0' или не достигнута указанная точность Выводит аргумент как адрес, формат зависит от модели памяти, в общем случае, включает 16‑ричные сегмент и смещение |
Функции ввода и вывода стандартной библиотеки Си позволяют читать данные из файлов или получать их с устройств ввода (например, с клавиатуры) и записывать данные в файлы или выводить их на различные устройства (например, на принтер).
Функции ввода/вывода делятся на три класса:
1. Ввод/вывод верхнего уровня (с использованием понятия "поток"). Для этих функций характерно, что они обеспечивают буферизацию работы с файлами. Это означает, что при чтении или записи информации обмен данными осуществляется не между программой и указанным файлом, а между программой и промежуточным буфером, расположенным в оперативной памяти.
При записи в файл информация из буфера записывается при его заполнении или при закрытии файла. При чтении данных программой информация берется из буфера, а в буфер она считывается при открытии файла и впоследствии каждый раз при опустошении буфера. Буферизация ввода/вывода выполняется автоматически, она позволяет ускорить выполнение программы за счет уменьшения количества обращений к сравнительно медленно работающим внешним устройствам.
Для пользователя файл, открытый на верхнем уровне, представляется как последовательность считываемых или записываемых байтов. Чтобы отразить эту особенность организации ввода/вывода, предложено понятие "поток" (англ. stream). Когда файл открывается, с ним связывается поток, выводимая информация записывается "в поток", а считываемая берется "из потока".
Когда поток открывается для ввода/вывода, он связывается со структурой типа FILE, определенной с помощью typedef в файле stdio.h. Эта структура содержит свою необходимую информацию о файле. При открытии файла с помощью стандартной функции fopen возвращается указатель на структуру типа FILE. Этот указатель, называемый указателем потока, используется для последующих операций с файлом. Его значение передается всем библиотечным функциям, используемым для ввода/вывода через этот поток.
Функции верхнего уровня одинаково работают в различных операционных средах, с их помощью можно писать переносимые программы.
2. Ввод/вывод для консольного терминала путем непосредственного обращения к нему. Функции для консоли и порта распространяют возможности функций ввода/вывода верхнего уровня на соответствующий класс устройств, добавляя новые возможности.
Они позволяют читать или записывать на консоль (терминал) или в порт ввода/вывода (например, порт принтера). Эти функции читают или записывают данные побайтно. Для ввода или вывода с консоли устанавливаются некоторые дополнительные режимы, например, ввод с эхо‑печатью символов или без нее, установка окна вывода, цветов текста и фона. Функции для консоли и порта являются уникальными для компьютеров, совместимых с IBM‑PC.
3. Ввод/вывод низкого уровня (с использованием понятия "дескриптор"). Функции низкого уровня не выполняют буферизацию и форматирование данных, они позволяют непосредственно пользоваться средствами ввода/вывода операционной системы.
При низкоуровневом открытии файла с помощью функции open с ним связывается файловый дескриптор (англ. handle). Дескриптор является целым значением, характеризующим размещение информации об открытом файле во внутренних таблицах операционной системы. Дескриптор используется при последующих операциях с файлом.
Функции низкого уровня из стандартной библиотеки обычно применяются при разработке собственных подсистем ввода/вывода. Большинство функций этого уровня переносимы в рамках некоторых систем программирования на Си, в частности, относящихся к операционной системе Unix и совместимым с ней.
Прототипы всех функций, описанных в табл. 8.10, содержатся в файле stdio.h.
Таблица 8.10. Функции в/в высокого уровня
Функция |
Краткое описание |
clearerr |
void clearerr(FILE *stream); очистка флага ошибки для потока stream |
fclose |
int fclose(FILE *stream); закрытие потока. Возвращает 0 в случае успеха, EOF если обнаружена ошибка |
fcloseall |
int fcloseall(void); закрытие всех открытых (на верхнем уровне) файлов. Возвращаемые значения: успех - общее число закрытых потоков, ошибка - EOF |
feof |
int feof(FILE *stream); проверка на конец потока (не 0, если достигнут) |
ferror |
int ferror(FILE *stream); проверка флажка ошибок потока (не 0 - ошибка) |
fflush |
int fflush(FILE *stream); сброс буфера потока на связанное с ним внешнее устройство. Успех - вернет 0, ошибка - вернет EOF |
fgetc |
int fgetc(FILE *stream); чтение символа из потока. На конец файла или ошибку возвращает EOF |
fgets |
char *fgets(char *s, int n, FILE *stream); чтение строки из потока (до перевода строки или достижения n-1 символов). Сохраняет символ новой строки ее последним байтом. Добавляет после него нулевой байт. В случае успеха возвращает указатель на прочитанную строку, в случае ошибки или конца файла - NULL |
flushall |
int flushall(void); сброс буферов всех потоков. Возвращает целое число открытых потоков ввода и вывода |
fopen |
FILE *fopen(const char *filename, const char *mode); открытие потока (открыть файл и связать его с потоком). Возвращает указатель на структуру FILE или NULL в случае ошибки. Строковая константа mode может иметь следующие значения: r открыть для чтения; w открыть для записи, существующий файл, если он есть, обнуляется; a открыть для записи в конец существующего файла. Если файл не существует, работает как "w"; r+ открыть существующий файл на чтение и запись; w+ создать новый файл для чтения и записи. Если файл с таким именем уже существует, он будет обнулен; a+ открыть для добавления или создать файл, если он не существует. Для указания на текстовый тип файла после любого из этих значений может быть добавлен символ "t", для указания на бинарный файл "b". В последнем случае следует работать с файлом методами fread и fwrite, а не использовать функции чтения/записи строк. Пример: FILE *f=fopen ("file.txt","r+t"); if (f==NULL) { //обработка ошибки } |
fprintf |
int fprintf(FILE *stream, const char *format <, argument, ...>); Запись данных в поток по формату. Число аргументов, начиная с третьего, зависит от числа элементов спецификации в строке формата и должно совпадать с ним. В случае успеха вернет число записанных байт, в случае ошибки - EOF |
fputc |
int fputc(int c, FILE *stream); запись символа с в поток. Пример: char msg[] = "Hello world"; int i = 0; while (msg[i]) { fputc(msg[i], stdout); i++; } |
fputs |
int fputs(const char *s, FILE *stream); запись строки, завершающейся нулевым байтом, в поток. Не добавляет символ новой строки и не копирует нулевой байт. В случае успеха возвращает последний записанный символ, в случае ошибки вернет EOF. Пример: fputs("Hello world\n", stdout); |
fread |
size_t fread(void *ptr, size_t size, size_t n, FILE *stream); неформатированное чтение данных из потока. Читает из потока stream n элементов данных по size байт каждый в память, на которую указывает ptr. Общее число прочитанных байт будет равно n*size. В случае успеха возвращает число прочитанных элементов (не байт!), в случае ошибки - 0 (точно не определено в спецификации) |
freopen |
FILE *freopen(const char *filename, const char *mode, FILE *stream); повторное открытие потока в новом режиме mode. Вернет указатель на поток или NULL в случае ошибки |
fscanf |
int fscanf(FILE *stream, const char *format<, address, ...>); чтение из потока по формату. Требования к аргументам совпадают с fprintf, все аргументы, начиная с третьего, должны быть адресами. В случае успеха вернет число успешно прочитанных, преобразованных и сохраненных полей ввода, вернет 0, если не удалось сохранить ни одно поле, вернет EOF при попытке чтения за пределами конца файла или строки (для sscanf) |
fseek |
int fseek(FILE *stream, long offset, int whence); Перемещение указателя файла в заданную позицию offset. Для текстового режима offset=0 или позиции, возвращаемой ftell. Для бинарного режима возможны и отрицательные значения. Константа whence определяет, откуда отсчитывать смещение: значение SEEK_SET - от начала файла; SEEK_CUR - от текущей позиции файлового указателя; SEEK_END - от конца файла. Вернет 0, если указатель успешно перемещен, в противном случае - ненулевую величину. Аналог: int fsetpos (FILE *stream. long offset); всегда считает смещение с начала файла. Успех - 0, иначе не‑ноль. |
ftell |
long int ftell(FILE *stream); получение текущей позиции указателя файла. Если файл бинарный, позиция отсчитывается от начала файла, начиная с 0. В случае ошибки вернет -1. Аналог: int fgetpos (FILE *stream. fpos_t *pos); запишет текущую позицию файлового указателя в pos. Тип fpоs_t здесь и далее определен как long. в случае успеха вернет 0, иначе не‑ноль. |
fwrite |
size_t fwrite(const void *ptr, size_t size, size_t n, FILE *stream); неформатированная запись данных в поток. Добавляет в поток stream n элементов данных по size байт каждый. Общее число записываемых байт = n*size. В случае успеха вернет число элементов (не байт!), записанных в поток, в случае ошибки - меньшее, чем n значение. |
getc |
int getc(FILE *stream); чтение символа из потока (реализуется через макроопределение). В случае успеха вернет прочитанный символ после преобразования к int без знака. В случае ошибки или достижения конца файла вернет EOF |
getchar |
int getchar(void); чтение символа из потока stdin (версия макро). Работает как getc для потока stdin |
gets |
char *gets(char *s); чтение строки из потока stdin. Заменяет символ конца строки нулевым байтом. Все символы, включая перевод строки, пишутся в строку s и возвращается указатель на нее. На конец файла или ошибку возвращает NULL |
printf |
int printf(const char *format<, argument, ...>); запись данных в поток stdout по формату. Требования - см. fprintf. В случае успеха вернет число записанных байт, в случае ошибки - EOF |
putc |
int putc(int c, FILE *stream); запись символа в поток stream (версия макро). Возвращает записанный символ c. В случае ошибки вернет EOF |
putchar |
int putchar(int c); запись символа в поток stdout (версия макро). Возвращает записанный символ c.В случае ошибки вернет EOF |
puts |
int puts(const char *s); запись строки s в поток stdout. Добавляет символ перевода строки. В случае успеха вернет неотрицательное число, на ошибку вернет EOF |
scanf |
int scanf(const char *format<, address, ...>); чтение данных из потока stdin по формату. Требования те же, что для fscanf |
setbuf |
void setbuf(FILE *stream, char *buf); управление буферизацией потока (назначить буфер потоку). Пример: char outbuf[BUFSIZ]; setbuf (stdout, outbuf); |
sprintf |
int sprintf(char *buffer, const char *format<, argument, ...>); запись данных в строку buffer по формату. Требования те же, что к fprintf. В случае успеха вернет число записанных байт, в случае ошибки - EOF. Не включает нулевой байт в число записанных символов. |
sscanf |
int sscanf (const char *buffer, const char *format<, address, ...>); чтение данных из строки buffer по формату. Требования те же, что к fscanf. |
ungetc |
int ungetc(int c, FILE *stream); вернуть символ с в буфер потока. Поток должен быть доступен для чтения. Символ будет прочитан из потока при следующем вызове getc или fread для этого потока. Возвращение одного символа безопасно, повторный вызов без обращения к функциям чтения из потока приведет к потере предыдущего символа. Вернет возвращенный символ (успех) или EOF (ошибка). |
В случае ошибки ряд функций устанавливает значение глобальной переменной errno из библиотеки errno.h.
Некоторые константы, определенные в stdio.h, могут быть полезны в программе. Они описаны в табл. 8.11.
Таблица 8.11. Константы библиотеки stdio.h
константа EOF |
код, возвращаемый как признак конца файла |
константа NULL |
значение указателя, который не содержит адрес никакого реально размещенного в оперативной памяти объекта |
константа BUFSIZ |
определяет размер буфера потока в байтах |
имя типа FILE |
структура, которая содержит информацию о потоке |
Для каждой выполняемой программы автоматически открываются пять потоков: стандартный ввод (stdin), стандартный вывод (stdout), стандартный вывод для сообщений об ошибках (stderr), стандартный последовательный порт (stdaux) и стандартное устройство печати (stdprn). По умолчанию первые три потока связаны с консольным терминалом, стандартными портами обычно служат последовательным порт и принтер. В файле stdio.h определены соответствующие указатели стандартных потоков:
extern FILE * stdin;
Ввод и вывод программы могут быть переопределены, например, средствами консоли Windows (символы перенаправления в/в <, > или >>), можно переопределять стандартные потоки также с помощью функции freopen.
В отличие от стандартных потоков, открытые файлы, для которых осуществляется высокоуровневый ввод/вывод, буферизуются по умолчанию. Для управления буферизацией служат функции setbuf и setvbuf. Их использованием можно сделать поток небуферизованным или связать буфер с небуферизованным до этого потоком. Буферы, размещенные в системе, недоступны пользователю, кроме тех, что получены с помощью setbuf или setvbuf. Буферы должны иметь постоянный размер, равный константе BUFSIZ из stdio.h.
Буферы можно сбросить в произвольный момент времени, используя функции fflush и flushall. Например, это имеет смысл делать перед каждым вводом с консоли с помощью функции scanf.
Потоки, с которыми завершена работа, следует закрыть функцией fclose или fcloseall. Стандартные потоки при этом не закрываются. Потоки, открытые программой, автоматически закрываются при ее завершении, однако, это может привести к потере данных при записи в поток, а также превышению предельного количества одновременно открытых потоков.
Работа с потоками происходит следующим образом. Функция открытия потока возвращает указатель на тип FILE, который используется при дальнейших обращениях к потоку.
Операции чтения и записи начинаются с текущей позиции в потоке, определяемой как указатель файла (англ. file pointer). Указатель файла изменяется после каждой операции чтения или записи. Например, при чтении символа из потока указатель продвигается на 1 байт, так что следующая операция начнется с первого несчитанного символа. Если поток открыт для добавления, указатель файла автоматически устанавливается на конец файла перед каждой операцией записи. Если поток не имеет указателя файла (например, консоль), функции позиционирования в файле (fsetpos, fseek) имеют неопределенный результат.
При ошибке в операции с потоком для него устанавливается в ненулевое значение флаг ошибки. Для определения того, произошла ли ошибка можно использовать макроопределение ferror. Флаг ошибки остается установленным до тех пор, пока не будет сброшен вызовом функции clearerr или rewind.
Перейдем к примерам. Следующий листинг иллюстрирует открытие файла для чтения, проверку ошибок системными средствами и посимвольное чтение текстовых данных из файла.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(void){
FILE *fp;
char ch,*s;
fp = fopen("c:\\test.txt", "rt");
if (ferror(fp)) { //тест потока на ошибку
sprintf (s,"Error %d",errno);
//встроенная переменная errno - номер ошибки
perror (s); //вызов системной печати
//сообщения об ошибке
fflush (stdin); getchar();
clearerr (fp); //сброс errno
exit (errno);
}
while (!feof(fp)) {
ch = fgetc(fp);
printf("%c",ch);
}
fflush (stdin); getchar();
fclose(fp);
return 0;
}
Во втором примере выполняется двоичная запись в файл, а проверка ошибок упрощена так, как чаще всего делается на практике - для определения того, удалось ли открыть файл, файловая переменная f сравнивается со значением NULL.
#include<stdio.h>
void error (int n) {
/* метод для обработки ошибок */
}
void main(void) {
FILE *f;
int k,i[]={1,2,3,4,5};
f=fopen("data.txt","r+b");
if (f==NULL) error (1);
//"Не могу открыть файл"
fseek (f,0L,SEEK_END);
long n;
fgetpos (f,&n);
if (n>0) error (2); //Файл не пуст
for(k=0;k<5;k++)
fwrite(&i[k],sizeof(int),1,f);
fclose(f);
}
Функции нижнего уровня не требуют включения файла stdio.h. Тем не менее, несколько общих констант, определенных в этом файле, могут оказаться полезными (например, признак конца файла EOF). Прототипы функций нижнего уровня содержатся в файле io.h.
В настоящее время низкоуровневый доступ к файлам имеет ограниченное применение, такое как написание системных функций и драйверов для устройств, программируемых на Си.
В табл. 8.12 кратко перечислены функции для низкоуровневого доступа к файлам.
Таблица 8.12. Низкоуровневый доступ к файлам
Функция |
Краткое описание |
close |
закрыть файл |
creat |
создать файл |
dup |
создать второй дескриптор (handle) для файла |
dup2 |
переназначить дескриптор (handle) для файла |
eof |
проверка на конец файла |
lseek |
позиционирование указателя файла в заданное место |
open |
открыть файл |
read |
читать данные из файла |
sopen |
открыть файл в режиме разделения доступа |
tell |
получить текущую позицию указателя файла |
write |
записать данные в файл |
Функции ввода/вывода для консоли используют специфические особенности IBM‑совместимого компьютера, такие как наличие специального видеоадаптера, и не являются переносимыми на другие типы компьютеров. Прототипы функций содержатся в файле conio.h. Консольные функции позволяют читать и записывать строки (cgets и cputs), форматированные данные (cscanf и cprintf) и отдельные символы. Функция kbhit определяет, было ли нажатие клавиши и позволяет определить наличие символов для ввода с клавиатуры до попытки чтения.
Во‑первых, существуют функции для работы с окном консоли, аналогичные библиотеке Crt Паскаля. В табл. 8.13 кратко перечислены основные из них.
Таблица 8.13. Функции для работы с окном консоли
Функция |
Краткое описание |
window |
void window(int left, int top, int right, int bottom); Устанавливает текущее окно консоли по указанным координатам |
clrscr |
void clrscr(void); очищает текущее окно |
clreol |
void clreol(void); очищает текущую строку окна от позиции курсора до конца |
delline |
void delline(void); удаляет строку окна, в которой установлен курсор |
insline |
void insline(void); вставляет пустую строку в позиции курсора |
gotoxy |
void gotoxy(int x, int y); перемещает курсор в указанные столбец (x) и строку (y) окна |
textbackground |
void textbackground(int newcolor); устанавливает указанный фоновый цвет окна. Цвета указываются номерами 0‑15 или названиями, определенными в conio.h (BLUE, GREEN и т. д.) |
textcolor |
void textcolor(int newcolor); устанавливает указанный цвет вывода текста в окне. Цвета указываются аналогично функции textbackground |
wherex |
int wherex(void); возвращает номер столбца окна, в котором находится курсор |
wherey |
int wherey(void); возвращает номер строки окна, в которой находится курсор |
Во‑вторых, в файле conio.h описаны прототипы ряда специфичных для Си функций (см. табл. 8.14).
Таблица 8.14. Функции ввода/вывода для консоли
Функция |
Краткое описание |
cgets |
char *cgets(char *str); чтение строки с консоли до комбинации CR/LF или достижения максимально возможного числа символов. Если cgets читает комбинацию CR/LF, она заменяет ее на нулевой байт перед сохранением строки. Перед вызовом функции str[0] должен быть установлен в максимально возможное число символов для чтения. В случае успеха str[1] содержит реально прочитанное число символов, возвращается указатель на str[2]. При чтении комбинации CR/LF она заменяется нулевым байтом. Нулевой байт добавляется в конец строки. Таким образом, длина буфера должна быть не меньше str[0]+2 байт. |
cprintf |
int cprintf(const char *format[, argument, ...]); запись данных на консольный терминал по формату. Требования к строке формата и последующим аргументам аналогичны функции fprintf. Не переводит '\n' (символ LF) в пару символов '\r\n' (CR/LF). В случае успеха вернет число выведенных символов, в случае ошибки - EOF. |
cputs |
int cputs(const char *str); вывод строки в текущее окно консольного терминала, определенное по умолчанию или функцией window. Не добавляет символов новой строки. |
getch |
int getch(void); чтение символа с консоли. Символ не отображается на экране (нет эхо‑печати). Вернет код символа. |
getche |
int getche(void); чтение символа с консоли с эхо‑печатью. Вернет код символа. |
kbhit |
int kbhit(void); проверка нажатия клавиши на консоли. Пример: while (!kbhit()) /*до нажатия клавиши */ ; cprintf("\r\nНажата клавиша..."); |
putch |
int putch(int c); Вывод символа на консольный терминал. Не переводит '\n' (символ LF) в пару символов '\r\n' (CR/LF). В случае успеха вернет символ, в случае ошибки - EOF |
ungetch |
int ungetch(int ch); возврат последнего прочитанного символа с консольного символа обратно с тем, чтобы он стал следующим символом для чтения. Ограничения те же, что для ungetc. |
Приведенный ниже пример демонстрирует чтение строки с помощью функции cgets.
char buffer[83];
char *p;
/* Место для 80 символов + нулевого байта */
buffer[0] = 81;
printf("Введите строку:");
p = cgets(buffer);
printf("\ncgets прочитала %d \
символов:\"%s\"\n", buffer[1], p);
printf("Возвращен указатель %p, \
buffer[0] содержит %p\n", p, &buffer);
В следующем примере строка текста печатается в центре стандартного окна консоли.
#include <conio.h>
int main(void){
clrscr();
gotoxy(35, 12);
textcolor (RED);
cprintf("Hello world");
getch();
return 0;
}
Наконец, приведенный далее код демонстрирует обработку нажатий клавиш для консольного терминала.
#include <stdio.h>
#include <conio.h>
int main(void) {
int ch;
do {
ch=getch();
if (ch=='\0') { //расширенный код
ch=getch();
switch (ch) {
case 72: printf ("\nUp"); break;
case 80: printf ("\nDown"); break;
case 75: printf ("\nLeft"); break;
case 77: printf ("\nRight"); break;
default: printf
("\nExtended key %2d",ch); break;
}
}
else {
switch (ch) {
case 13: printf ("\nEnter"); break;
case 27: printf ("\nEsc"); break;
default: printf ("\nKey %4d",ch);
break;
}
}
} while (ch!=27);
}
8.10. Работа с каталогами файловой системы
Прототипы функций, описанных в табл. 8.15, содержатся в стандартном заголовочном файле dir.h. Не все функции описаны в стандарте языка Си, но обычно все они предоставляются комилятором.
Таблица 8.15. Функции для работы с каталогами
Функция |
Краткое описание |
chdir |
int chdir(const char *path); изменение текущего каталога, заданного полным или относительным путем path. Возвращает 0 в случае успеха, -1 в случае ошибки |
getcwd |
char *getcwd(char *buf, int buflen); получить имя текущего рабочего каталога. В случае успеха buf!=NULL. Ограничения, такие как MAXPATH (макс. длина пути к каталогу) определены в <dir.h>. Аналог - int getcurdir (int drive, char *directory); Диск по умолчанию кодируется как 0, 1 обозначает диск A: и т. д. |
mkdir |
int mkdir(const char *path); пытается создать новый каталог, заданный полным или относительным путем path. В случае успеха вернет 0, иначе -1 |
rmdir |
int rmdir(const char *path); удаление каталога, заданного путем path. В случае успеха вернет 0, иначе -1 |
findfirst |
int findfirst(const char *pathname, struct ffblk *ffblk, int attrib); начинает поиск файла по шаблону имени pathname. для работы с поиском определен структурный тип ffblk: struct ffblk { long ff_reserved; long ff_fsize; //размер файла unsigned long ff_attrib;//атрибуты unsigned short ff_ftime; //время unsigned short ff_fdate; // дата char ff_name[256]; //найденное имя }; Атрибуты определены в библиотеке dos.h: FA_RDONLY Read-only FA_HIDDEN Hidden FA_SYSTEM System FA_LABEL Volume label FA_DIREC Directory FA_ARCH Archive Поле ff_ftime определяет время следующим образом: биты от 0 до 4 обозначают секунды, деленные на 2, биты 5-10 - минуты, биты 11-15 - часы. Поле ff_fdate определяет дату следующим образом: биты 0-4 обозначают день месяца, биты 5-8 - номер месяца, биты 9-15 - год с 1980 (например, 9 означает 1989). В случае успеха вернет 0, иначе -1 |
findnext |
int findnext(struct ffblk *ffblk); продолжает поиск файла по ранее указанному для findfirst шаблону имени |
fnmerge |
void fnmerge(char *path, const char *drive, const char *dir, const char *name, const char *ext); создает полное имя файла из отдельных компонент |
fnsplit |
int fnsplit(const char *path, char *drive, char *dir, char *name, char *ext); разбивает полное имя файла на отдельные компоненты. Вернет целое число, биты которого показывают наличие отдельных компонент |
getdisk |
int getdisk(void); возвращает номер текущего диска. 0 обозначает диск A:, 1 - B: и т. д. |
searchpath |
char *searchpath(const char *file); выполняет поиск файла в каталогах, перечисленных в системной переменной PATH. Если файл найден, вернет полный путь к нему в статическом буфере (обновляется при каждом вызове функции) |
setdisk |
int setdisk(int drive); задает текущее дисковое устройство. Диски обозначаются как для getdisk. Возвращает общее число дисков, доступных в системе |
Приведенный далее пример формирует листинг из всех файлов текущего каталога.
#include <stdio.h>
#include <dos.h>
#include <dir.h>
int main(void) {
struct ffblk ffblk;
int done;
printf("Directory listing of *.*\n");
done = findfirst("*.*",&ffblk,
FA_ARCH|FA_DIREC);
while (!done) {
unsigned t=ffblk.ff_ftime;
char s=(t&0x001F)<<1, m=(t&0x07E0)>>5,
h=(t&0xF800)>>11;
t=ffblk.ff_fdate;
char d=(t&0x001F), mon=(t&0x01E0)>>5;
int y=(t&0xFE00)>>9;
printf("%s %15ld \
%02d/%02d/%4d,%02d:%02d:%02d\n",
ffblk.ff_name,ffblk.ff_fsize,
d,mon,y+1980,h,m,s);
done = findnext(&ffblk);
}
getchar(); return 0;
}
В следующем примере иллюстрируется работа с функцией fnsplit.
#include <stdlib.h>
#include <stdio.h>
#include <dir.h>
char *s;
char drive[MAXDRIVE];
char dir[MAXDIR];
char file[MAXFILE];
char ext[MAXEXT];
int flags;
s=getenv("COMSPEC");
flags=fnsplit(s,drive,dir,file,ext);
printf("Command processor info:\n");
if(flags & DRIVE)
printf("\tdrive: %s\n",drive);
if(flags & DIRECTORY)
printf("\tdirectory: %s\n",dir);
if(flags & FILENAME)
printf("\tfile: %s\n",file);
if(flags & EXTENSION)
printf("\textension: %s\n",ext);
8.11. Операции над файлами
В табл. 8.16 описаны основные стандартные функции, служащие для работы с файлами. В левом столбце таблицы под именем функции указаны имена библиотечных файлов, содержащих прототипы.
Таблица 8.16. Функции для работы с файлами
Функция |
Краткое описание |
access <io.h> |
int access(const char *filename, int amode); определение прав доступа к файлу. Допустимы значений amode=06 (проверка на чтение и запись), 04 (чтение), 02 (запись), 01 (выполнение), 00 (проверка на существование файла) |
chmod <io.h> |
int chmod(const char *path, int amode); изменение прав доступа к файлу. Допустимые значения amode определены в sys\stat.h: S_IWRITE (разрешение на запись), S_IREAD (на чтение), S_IREAD | S_IWRITE (то и другое). Вернет 0 в случае успеха, -1 при ошибке |
mktemp <dir.h> |
char *_mktemp(char *template); генерация уникального имени файла. Пример: char *fname = "TXXXXXX", *ptr; ptr = mktemp(fname); |
remove <stdio.h> |
int remove(const char *filename); удаление файла. Вернет 0 в случае успеха, -1 при ошибке |
rename <stdio.h> |
int rename(const char *oldname, const char *newname); переименование файла из oldname в newname. Вернет 0 в случае успеха, -1 при ошибке |
8.12. Использование вызовов операционной системы
Для доступа к функциям операционных систем, совместимых с MS‑DOS предназначена библиотека dos.h. Можно выделить следующие основные группы функций этой библиотеки:
· Обработка прерываний (int86, int86x, getinterrupt, disable, enable, getvect, setvect);
· Работа с сегментами памяти (peek, poke, MK_FP);
· Доступ к секторам диска (absread, abswrite);
· Доступ к файловым таблицам FAT (getfat, getfatd, getdfree);
· Работа с портами (inport, outport).
Компилятор может предоставлять также возможности для обращения к базовой подсистеме ввода/вывода операционной системы BIOS. Основные функции кратко перечислены в табл. 8.17. Их прототипы содержатся в файле bios.h.
Таблица 8.17. Основные функции для работы с BIOS
Функция |
Краткое описание |
bioscom |
управление последовательным каналом |
biosdisk |
управление диском |
biosequip |
проверка конфигурации аппаратуры |
bioskey |
управление клавиатурой |
biosmemory |
возвращает объем оперативной памяти |
biosprint |
управление устройством печати |
biostime |
управление BIOS-таймером |
8.13. Управление процессами
Функции этой группы позволяют приложению выполнять дочерние процессы, программно выполнять команды операционной системы, а также вырабатывать код завершения. Прототипы функций управления процессами объявлены в файле process.h. Основные функции описаны в табл. 8.18.
Таблица 8.18. Функции для управления процессами
Функция |
Краткое описание |
abort |
void abort(void); прерывает текущий процесс (прототип содержится также в stdlib.h) |
exit |
void exit(int status); завершает процесс с кодом errorlevel=status (прототип есть также в stdlib.h) |
execl
|
int execl(char *path, char *arg0, *arg1, ..., *argn, NULL); позволяет выполнить порождаемый процесс со списком аргументов. Существуют разновидности функции для передачи процессу параметров командной строки, использования PATH, системного окружения и т. п. |
spawnl |
int spawnl(int mode, char *path, char *arg0, arg1, ..., argn, NULL); позволяет выполнить порождаемый процесс со списком аргументов. Величина mode определяет режим запуска дочернего процесса |
system |
int system(const char *command); выполнение команды ОС |
8.14 Поиск и сортировка
Си содержит ряд стандартных функций, позволяющих упростить задачи сортировки и поиска данных. Их прототипы описаны в библиотеке stdlib.h. Библиотечные функции, предназначенные для поиска и сортировки в массиве, перечислены в табл. 8.19.
Таблица 8.19. Функции поиска и сортировки
Функция |
Краткое описание |
bsearch |
Выполняет бинарный поиск в массиве |
lfind |
выполняет линейный поиск для заданного значения |
lsearch |
выполняет линейный поиск для заданного значения, которое добавляется в массив, если не найдено |
qsort |
выполняет быструю сортировку |
8.15. Функции для работы с датой и временем
Прототипы этой группы функций описаны в библиотеках dos.h и time.h. Функции описаны в табл. 8.20.
Таблица 8.20.. Функции для работы с датой и временем
Функция |
Краткое описание |
getdate |
void getdate(struct date *datep); получает системную дату как структуру типа date со следующими полями: struct date{ int da_year; //год char da_day; //день месяца char da_mon; //месяц (1= январь) }; |
gettime |
void gettime(struct time *timep); получает системное время как структуру типа time со следующими полями: struct time { unsigned char ti_min; //минуты unsigned char ti_hour; //часы unsigned char ti_hund; //сотые доли секунды unsigned char ti_sec; //секунды }; |
setdate |
void setdate(struct date *datep); устанавливает системную дату |
settime |
void settime(struct time *timep); устанавливает системное время |
time |
long time(long *timer); получает системное время в "тиках" - секундах с начала "эры Unix" 1 января 1970 г. в полночь по Гринвичу |
гостевая; E-mail |