Функцию alloc легче всего реализовать, если условиться, что она будет выдавать куски некоторого большого массива типа char , который мы назовем allocbuf . Этот массив отдадим в личное пользование функциям alloc и afree . Так как они имеют дело с указателями, а не с индексами массива, то другим программам знать его имя не нужно. Кроме того, этот массив можно определить в том же исходном файле, что и alloc и afree , объявив его static , благодаря чему он станет невидимым вне этого файла. На практике такой массив может и вовсе не иметь имени, поскольку его можно запросить с помощью malloc у операционной системы и получить указатель на некоторый безымянный блок памяти.
Естественно, нам нужно знать, сколько элементов массива allocbuf уже занято. Мы введем указатель allocp , который будет указывать на первый свободный элемент. Если запрашивается память для n символов, то alloc возвращает текущее значение allocp (т. е. адрес начала свободного блока) и затем увеличивает его на n , чтобы указатель allocp указывал на следующую свободную область. Если же пространства нет, то alloc выдает нуль. Функция afree(p) просто устанавливает allocp в значение p , если оно не выходит за пределы массива allocbuf .
Перед вызовом allос :
После вызова alloc :
#define ALLOCSIZE 10000 /* размер доступного пространства */
static char allocbuf[ALLOCSIZE]; /* память для alloc */
static char *allocp = allocbuf; /* указатель на своб. место */
char *alloc(int n) /* возвращает указатель на n символов */
{
if (allocbuf + ALLOCSIZE - allocp ›= n) {
allocp += n; /* пространство есть */
return allocp - n; /* старое p */
} else /* пространства нет */
return 0;
}
void afree(char *p) /* освобождает память, на которую указывает p */
{
if (р ›= allocbuf && p ‹ allocbuf + ALLOCSIZE)
allocp = p;
}
В общем случае указатель, как и любую другую переменную, можно инициализировать, но только такими осмысленными для него значениями, как нуль или выражение, приводящее к адресу ранее определенных данных соответствующего типа. Объявление
static char *allocp = allocbuf;
определяет allocp как указатель на char и инициализирует его адресом массива allocbuf , поскольку перед началом работы программы массив allocbuf пуст. Указанное объявление могло бы иметь и такой вид:
static char *allocp = &allocbuf[0];
поскольку имя массива и есть адрес его нулевого элемента. Проверка
if (allocbuf + ALLOCSIZE - allocp ›= n) {/* годится */
контролирует, достаточно ли пространства, чтобы удовлетворить запрос на n символов. Если памяти достаточно, то новое значение для allocp должно указывать не далее чем на следующую позицию за последним элементом allocbuf . При выполнении этого требования alloc выдает указатель на начало выделенного блока символов (обратите внимание на объявление типа самой функции). Если требование не выполняется, функция alloc должна выдать какой-то сигнал о том, что памяти не хватает. Си гарантирует, что нуль никогда не будет правильным адресом для данных, поэтому мы будем использовать его в качестве признака аварийного события, в нашем случае нехватки памяти.
Указатели и целые не являются взаимозаменяемыми объектами. Константа нуль - единственное исключение из этого правила: ее можно присвоить указателю, и указатель можно сравнить с нулевой константой. Чтобы показать, что нуль - это специальное значение для указателя, вместо цифры нуль, как правило, записывают NULL- константу, определенную в файле ‹stdio.h› (I.B.: вообще-то эта константа определена в ‹stddef.h› или ‹string.h›). С этого момента и мы будем ею пользоваться. Проверки
if (allocbuf + ALLOCSIZE - allocp ›= n) {/* годится */
и
if (p ›= allocbuf && p ‹ allocbuf + ALLOCSIZE)
демонстрируют несколько важных свойств арифметики с указателями. Во- первых, при соблюдении некоторых правил указатели можно сравнивать.
Если p и q указывают на элементы одного массива, то к ним можно применять операторы отношения ==, !=, ‹, ›=и т. д. Например, отношение вида
p ‹ q
истинно, если p указывает на более ранний элемент массива, чем q . Любой указатель всегда можно сравнить на равенство и неравенство с нулем. А вот для указателей, не указывающих на элементы одного массива, результат арифметических операций или сравнений не определен. (Существует одно исключение: в арифметике с указателями можно использовать адрес несуществующего "следующего за массивом" элемента, т. е. адрес того "элемента", который станет последним, если в массив добавить еще один элемент.)
Читать дальше