Правильный способ выделения и освобождения массивов указателей на массивы
Я хочу создать массив указателей на массивы из 3 поплавков. Каков правильный способ сделать это?
float *array1[SIZE]; // I think it is automatically allocated
// OR
float **array1 = calloc(SIZE, sizeof(float*));
free(array1);
for (int i = 0; i < SIZE; i++) {
array1[i] = (float[]){0,0,0};
// OR
array1[i] = calloc(3, sizeof(float));
}
Тогда как я могу освободить данные? Я почти уверен, что только free(array1);
не будет работать, поэтому я бы освободил каждый указатель в массиве, затем освободил массив, или, поскольку я выделил три поплавков, я бы освободил каждый float, затем каждый 3 массива float, затем весь массив???
Ответы
Ответ 1
Общее правило состоит в том, что для каждого вызова malloc()
или calloc()
вам нужно выполнить вызов free()
на возвращаемом указателе.
Если вам нужен двухмерный массив с известным размером компиляции, просто используйте двухмерный массив! float val[5][3]
отлично.
Если вам нужен двумерный массив, и вы не знаете его размер во время компиляции, вы, скорее всего, захотите использовать стандартный односегментный calloc() и соответствующий получатель.
#define ARR_COLUMNS 10
#define ARR_ROWS 10
float* arr = calloc (ARR_COLUMNS * ARR_ROWS, sizeof(float));
int get(float* arr, int x, int y) {
if (x<0 || x>= ARR_COLUMNS) return 0;
if (y<0 || y>= ARR_ROWS) return 0;
return arr[ARR_COLUMNS*y+x];
}
void set (int* arr, int x, int y, float val) {
if (x<0 || x>= ARR_COLUMNS) return;
if (y<0 || y>= ARR_ROWS) return;
arr[ARR_COLUMNS*y+x] = val;
}
Конечно, замените определения соответствующими переменными.
Сделав это, вы:
- сэкономить дорогостоящие ассигнования и освобождает
- имеют менее фрагментированную память.
- упростить возможные вызовы realloc
- убедитесь, что данные кэшируются лучше и доступны без общей проблемы [x] [y] vs [y] [x] итерации].
Ответ 2
Если вы знаете размер массива во время компиляции (и вы это делаете, если SIZE
- константа времени компиляции), вы должны просто объявить двумерный массив. Вам не нужно освобождать это вообще (и не должно).
float array1[SIZE][3];
Вам нужно использовать calloc
и создать массив указателей, только если размеры не известны во время компиляции. В этом случае для каждого вызова calloc
должен быть один вызов free
. И поскольку вы не можете использовать массив после его освобождения, вам нужно освободить массивы строк до того, как вы освободите array1
.
float **array1 = calloc(nrows, sizeof (float *));
for (int i=0; i < nrows; i++)
array1[i] = calloc(3, sizeof(float));
// Use it...
// Now free it
for (int i=0; i < nrows; i++)
free(array1[i]);
free(array1);
Изменить:, если вы не будете переставлять указатели (например, для сортировки строк на месте), вы можете сделать все это всего одним calloc
(и одним вызовом до free
):
float (*array1)[3] = calloc(3*nrows, sizeof (float));
Это потому, что количество столбцов известно во время компиляции и что нужно знать всю арифметику указателя. Затем вы можете писать такие вещи, как array1[i][j]
, и вы все равно можете обойти array1[i]
, как если бы это был реальный указатель на строку. C отлично подходит, воспользуйтесь им!
Ответ 3
Я хочу создать массив указателей на массивы из 3 поплавков. Каков правильный способ сделать это?
Зачем вам нужен array of pointers to arrays
? Разве не было бы array of arrays
? (Имейте в виду, что массивы уже похожи на указатели, они не передаются по значению, а адрес первого элемента передается, когда массив передается как аргумент функции).
// stack allocation, no need to free
float array[SIZE][3];
for (int i = 0; i < SIZE; i++) {
// do something with array[i][0], array[i][1], array[i][2]
}
Тогда как я могу освободить данные?
В этом случае вы не будете, так как данные выделены в стек (будут автоматически очищены один раз из области). Просто помните, что эмпирическое правило состоит в том, что для каждого выделенного выделения памяти необходимо соответствующее свободное. Поэтому, если вы выделяете память для массива float, как в
float* arr = malloc(sizeof(float) * 3); // array of 3 floats
Тогда вам нужно только позвонить бесплатно на массив, который вам malloc'd
, не нужно освобождать отдельные поплавки. Если вы выполняете вложенное распределение, как в
// array of length SIZE, consisting of float pointers
float** arr = malloc(sizeof(float*) * SIZE);
// allocate the array of 3 floats at each index
for (int i = 0; i < SIZE; i++)
arr[i] = malloc(sizeof(float) * 3);
Затем вам нужно будет выполнить free
для каждого malloc
, как в
// free the individual arrays
for (int i = 0; i < SIZE; i++)
free(arr[i]);
// free the array of arrays
free(arr);
Урок, который нужно убрать, заключается в том, чтобы избежать динамического распределения массивов вместе. Придерживайтесь с помощью std::vector
или массивов, распределенных по стекам.