Pers.narod.ru. Обучение. Лекции по Си. Глава 8

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.7. Функции ввода/вывода высокого уровня

Прототипы всех функций, описанных в табл. 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);

}                     

 

8.8. Функции ввода/вывода нижнего уровня

Функции нижнего уровня не требуют включения файла stdio.h. Тем не менее, несколько общих констант, определенных в этом файле, могут оказаться полезными (например, признак конца файла EOF). Прототипы функций нижнего уровня содержатся в файле io.h.

В настоящее время низкоуровневый доступ к файлам  имеет ограниченное применение, такое как написание системных функций и драйверов для устройств, программируемых на Си.

В табл. 8.12 кратко перечислены функции для низкоуровневого доступа к файлам.

 

Таблица 8.12. Низкоуровневый доступ к файлам

Функция

Краткое описание

close

закрыть файл

creat

создать файл

dup

создать второй дескриптор (handle) для файла

dup2

переназначить дескриптор (handle) для файла

eof

проверка на конец файла

lseek

позиционирование указателя файла в заданное место

open

открыть файл

read

читать данные из файла

sopen

открыть файл в режиме разделения доступа

tell

получить текущую позицию указателя файла

write

записать данные в файл

 

8.9. Функции ввода/вывода с консольного терминала

Функции ввода/вывода для консоли используют специфические особенности 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 г. в полночь по Гринвичу

 

 

Рейтинг@Mail.ru
вверх гостевая; E-mail
Hosted by uCoz