Я думаю, вы говорите, что хотите «увеличивать» массив по одному элементу за раз.
realloc()
- это опция в C для массива целых чисел (*).
size_t n=0;//Length of array.
int* arr=NULL;
//Some code...
//Now we want to grow our array
//This is probably in a loop or called in a function from a loop (not shown).
++n;
int* temp=realloc(arr,n*sizeof(int));
if(temp==NULL){
free(arr);
return 1;//Error...
}
arr=temp;
//More code....
//Now we're done with arr.
free(arr);
В первом проходе arr
равен NULL
, а realloc(NULL,..)
просто действует как malloc()
.Но при последующих проходах, когда arr
не равен NULL
, realloc
пытается увеличить массив «на месте» (если в конце имеется память).Если нет, он пытается выделить место в другом месте, а затем копирует существующие данные и освобождает старое местоположение.Если он не может перераспределить, он ничего не делает и возвращает NULL
, поэтому вы несете ответственность за освобождение этого пространства (free(arr)
).
Примечание: всегда выделяйте возврат realloc()
для временной переменной(т.е. не первый аргумент).В противном случае, если выделение не удастся, вы вытекли из существующей памяти.
realloc
- это быстрый прием для уменьшения объема выделения и копирования, необходимого для увеличения массива - альтернатива.
Если это не такдостаточно эффективной, нормальная модель заключается в том, чтобы отслеживать «емкость» и «длину» примерно так:
const size_t chunk=10;
size_t cap=0;
size_t n=0;
int* arr=NULL;
++n;
if(n>cap){
cap+=chunk;
int* temp=realloc(arr,cap);
if(temp==NULL){
free(arr);
return 1;//Error
}
}
//Later...
free(arr);
Это дает вам преимущество в том, что вы можете настраивать, сколько «лишних» накладных расходов вам нужно настроитькак часто происходит перераспределение.Другая стратегия - cap=cap*2;
, хотя, очевидно, пространство имеет тенденцию к экспоненциальному росту!Можно написать книгу о стратегиях для этой классики.Часто можно выделить достаточно для наиболее реалистичных случаев одним ударом, но по-прежнему обрабатывать исключительные случаи.
У вас также есть возможность выделить обратно, чтобы восстановить свободное пространство, если вы знаете, что массив теперь является полноразмерным.
В случае, если это не очевидно, если массив перемещается, любые указатели на него или на него должны быть перенаправлены.Это аргумент только для индексации с [.]
.Вам нужно будет удерживать старое местоположение, пока вы не выполните перенаправление.ptr=temp+(ptr-arr)
заставляет ptr
указывать на новое местоположение элемента, на который указывает ptr
.
Это стандартный подход, встречающийся в Java ArrayList
и большинстве реализаций std::vector<>
.Это инженерный компромисс.Преимущества массива (O (1) произвольного доступа по индексу) и связанного списка (O (1) роста).
Это асимптотически все еще линейно, но в практических случаях дает большую выгоду.Информатика занимается асимптотическими скоростями и проектированием с фактическими значениями в конечных реальных случаях.
(*) Копия является необработанной копией памяти.Это нормально для int
, но сложные структуры struct не могут быть скопированы на сумму, равную memmove
, или требуют дополнительных усилий.