*/ nvtab.nameval =
(Nameval *) malloc(NVINIT * sizeof(Nameval));
if (nvtab.nameval == NULL)
return -1; nvtab.max = NVINIT; nvtab.nval = 0;
} else if (nvtab.nval >= nvtab.max) { /* расширить
*/ nvp = (Nameval *) realloc(nvtab.nameval,
(NVGROW*nvtab.max) * sizeof(Nameval));
if (nvp == NULL)
return -1;
nvtab.max *= NVGROW;
nvtab.nameval = nvp; }
nvtab.nameval[nvtab.nval]
= newname; return nvtab.
Функция addname возвращает индекс только что добавленного элемента или -1 в
случае возникновения ошибки.
Вызов reallос увеличивает массив до новых размеров, сохраняя существующие
элементы, и возвращает указатель на него или NULL при недостатке памяти.
Удвоение размера массива при каждом вызове realloc сохраняет средние
"ожидаемые" затраты на копирование
элемента постоянными; если бы массив
увеличивался каждый раз только на один элемент, то производительность была бы
порядка 0(п2). Поскольку при перераспределении памяти адрес массива может
измениться, то программа должна обращаться к элементам массива по индексам, а
не через указатели. Заметьте, что код не таков:
? nvtab.nameval = (Nameval *) realloc(nvtab.nameval,
? (NVGROW*nvtab.jnax) * sizeof (Nameval));
потому что при такой форме если
вызов realloc не сработает, то исходный массив
будет утерян.
Мы задаем очень маленький начальный размер массива (NVINIT = 1). Это
заставляет программу увеличивать массив почти сразу, что гарантирует проверку
данной части программы. Начальное значение может быть и увеличено, когда
программа начнет реально использоваться, однако затраты на стартовое
расширение массива ничтожны.
Значение, возвращаемое realloc, не обязательно
должно приводиться к его
настоящему типу, поскольку в С нетипизированные указатели (void *) приводятся к
любому типу указателя автоматически. Однако в C++ это не так; здесь
преобразование обязательно. Можно поспорить, что безопаснее: преобразовывать
типы (это честнее и понятнее) или не преобразовывать (поскольку в преобразовании
легко может закрасться ошибка). Мы выбрали преобразование, потому что тогда
программа корректна как в С, так и в C++. Цена такого решения — уменьшение
количества проверок на ошибки компилятором С, но это неважно, когда мы
производим дополнительные проверки с помощью двух компиляторов.
Удаление элемента может быть более сложной операцией, поскольку нам нужно
решить, что же делать с образовавшейся "дыркой" в массиве. Если
порядок
элементов не имеет значения, то проще всего переставить последний элемент на
место удаленного. Однако, если порядок должен быть сохранен, нам нужно сдвинуть
элементы после "дырки" на одну позицию: