Стандартная библиотека Си

Основные отличия стандартной библиотеки Си от других языков состоят в следующем:

· более сильная интеграция с языком. Так, в самом языке Си нет никаких средств ввода‑вывода, поэтому ни одна программа не может быть написана без использования функций стандартной библиотеки;

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

· многие функции имеют несколько "подфункций", решающих схожие задачи и обычно отличающихся одной буквой в названии, например, 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;


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: