Основные отличия стандартной библиотеки Си от других языков состоят в следующем:
· более сильная интеграция с языком. Так, в самом языке Си нет никаких средств ввода‑вывода, поэтому ни одна программа не может быть написана без использования функций стандартной библиотеки;
· несмотря на то, что существуют стандарты языка Си, ряд функций уникален для той или иной системы программирования;
· многие функции имеют несколько "подфункций", решающих схожие задачи и обычно отличающихся одной буквой в названии, например, 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 (islower(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. В компиляторах от Borland прототипы указанных функций содержит также библиотека string.h.
Таблица 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, но корректно обрабатывает перекрывающиеся области памяти |
Прототипы функций обработки строк содержатся в файле string.h. Все функции работают со строками, завершающимися нулевым байтом '\0'. Для функций, возвращающих указатели, в случае ошибки возвращается NULL.
Типизированный модификатор size_t определен в ряде стандартных библиотек следующим образом:
typedef unsigned size_t;
Основные библиотечные функции описаны в табл. 8.3.
Таблица 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;
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). Этот механизм позволяет динамически запрашивать из программы дополнительные области оперативной памяти.
Работа функций динамического распределения памяти различается для различных моделей памяти, поддерживаемых системой программирования (см. п. 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(20);
printf("String is %s\nAddress is %p\n",
str, str);
str = (char *) realloc(str, 40);
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 (int n, int m) {
//выделяет память для матрицы n*m
int **p=NULL;
p=(int **)malloc(n*sizeof(int *));
if (p==NULL) error (2);
for (int i=0; i<n; i++) {
p[i]=(int *)malloc(m*sizeof(int));
if (p[i]==NULL) error (2);
}
return p;
}
/*... */
int n=0,m=0;
printf ("\n Enter number of items:");
scanf ("%d %d",&n,&m);
if (n<2 || m<2) error (1);
int **p=allocatematrix(n,m);
for (int i=0; i<n; i++) {
printf ("\n ");
for (int j=0; j<m; j++) {
p[i][j]=i+j; printf ("%4d ",p[i][j]);
}}
Правильный порядок освобождения занятой памяти для динамической матрицы будет таков:
for (long i=n-1; i>-1; i--) free (p[i]);
free (p);
Ввод значений многомерных вещественных массивов с клавиатуры также требует осторожности. Код вида
scanf ("%f",&p[i][j]);
следует заменять на
float f; scanf ("%f",&f); p[i][j]=f;