Одна долгосрочная проблема заключается в том, что вы не возвращаете обновленный указатель массива из функции addPointerToArray()
:
void addPointerToArray(Vertex v1, Vertex out[], int *size)
{
int newSize = *size;
newSize++;
out = realloc(out, newSize * sizeof(Vertex));
out[(*size)] = v1;
// Update Size
*size = newSize;
}
Когда вы перераспределяете пространство, оно может перемещаться в новое место, поэтому возвращаемое значение из realloc()
не обязательно должно совпадать с указателем ввода. Это может работать, когда при добавлении в массив другие выделения памяти не выполняются, поскольку realloc()
будет расширять существующее выделение, пока есть место для этого, но оно ужасно завершится неудачей, когда вы начнете выделять другие данные при чтении вершин. , Есть несколько способов исправить это:
Vertex *addPointerToArray(Vertex v1, Vertex out[], int *size)
{
int newSize = *size;
newSize++;
out = realloc(out, newSize * sizeof(Vertex));
out[(*size)] = v1;
// Update Size
*size = newSize;
return out;
}
и вызов:
out = addPointerToArray(vertexToAdd, out, size);
В качестве альтернативы, вы можете передать указатель на массив:
void addPointerToArray(Vertex v1, Vertex **out, int *size)
{
int newSize = *size;
newSize++;
*out = realloc(*out, newSize * sizeof(Vertex));
(*out)[(*size)] = v1;
// Update Size
*size = newSize;
}
и вызов:
out = addPointerToArray(vertexToAdd, &out, size);
Ни одна из этих переписок не устраняет утечку памяти. Проблема в том, что если вы перезаписываете значение, которое вы передаете в realloc()
, возвращаемым значением, но realloc()
не удается, вы теряете указатель на (все еще) выделенный массив - утечка памяти. Когда вы используете realloc()
, используйте идиому вроде:
Vertex *new_space = realloc(out, newSize * sizeof(Vertex));
if (new_space != 0)
out = new_space;
else
...deal with error...but out has not been destroyed!...
Обратите внимание, что использование realloc()
для добавления одного нового элемента за раз приводит к (может привести к) квадратичному поведению. Было бы лучше выделить большой кусок памяти - например, удвоить выделенное пространство:
int newSize = *size * 2;
Если вас беспокоит перераспределение, в конце цикла чтения вы можете использовать realloc()
, чтобы уменьшить выделенное пространство до точного размера массива. Тем не менее, есть еще немного бухгалтерского учета, чтобы сделать; вам нужно указать значения: количество вершин, выделенных массиву, и количество фактически используемых вершин.
Наконец, по крайней мере пока, обратите внимание, что вы действительно должны быть безжалостно последовательными и использовать addPointerToArray()
, чтобы добавить первые три записи в массив. Я бы, вероятно, использовал что-то похожее на этот (не проверенный) код:
struct VertexList
{
size_t num_alloc;
size_t num_inuse;
Vertex *list;
};
void initVertexList(VertexList *array)
{
// C99: *array = (VertexList){ 0, 0, 0 };
// Verbose C99: *array = (VertexList){ .num_inuse = 0, .num_alloc = 0, .list = 0 };
array->num_inuse = 0;
array->num_alloc = 0;
array->list = 0;
}
void addPointerToArray(Vertex v1, VertexList *array)
{
if (array->num_inuse >= array->num_alloc)
{
assert(array->num_inuse == array->num_alloc);
size_t new_size = (array->num_alloc + 2) * 2;
Vertex *new_list = realloc(array->list, new_size * sizeof(Vertex));
if (new_list == 0)
...deal with out of memory condition...
array->num_alloc = new_size;
array->list = new_list;
}
array->list[array->num_inuse++] = v1;
}
При этом используется нелогичное свойство realloc()
, которое он будет делать malloc()
, если переданный указатель будет нулевым. Вместо этого вы можете проверить array->list == 0
и использовать malloc()
тогда и realloc()
в противном случае.
Вы можете заметить, что эта структура также упрощает вызывающий код; вам больше не нужно иметь дело с отдельным int *size;
в основной программе (и ее распределением памяти); размер эффективно объединяется в структуру VertexList
как num_inuse
. Основная программа может теперь запуститься:
int main(void)
{
VertexList array;
initVertexList(&array);
addPointerToArray((Vertex){ 1, 1 }, &array); // C99 compound literal
addPointerToArray((Vertex){ 2, 2 }, &array);
addPointerToArray((Vertex){ 3, 3 }, &array);
addPointerToArray((Vertex){ 9, 9 }, &array);
for (int i = 0; i < array->num_inuse; i++)
printf("Vertex %d: (%d, %d)\n", i, array->list[i].x, array->list[i].y, i);
return 0;
}
(Это случайно, что эта последовательность будет вызывать выделение памяти только один раз, потому что новый размер (old_size + 2) * 2
выделяет 4 элемента в массив в первый раз. Легко осуществить перераспределение, добавив новую точку или уточнив формула (old_size + 1) * 2
, или ...
Если вы планируете восстанавливаться после сбоя выделения памяти (а не просто выходить из него, если это произойдет), вам следует изменить addPointerToArray()
, чтобы он возвращал состояние (успешно, не успешно).
Кроме того, имя функции, вероятно, должно быть addPointToArray()
или addVertexToArray()
или даже addVertexToList()
.