Правильный способ выделения и освобождения массивов указателей на массивы

Я хочу создать массив указателей на массивы из 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 или массивов, распределенных по стекам.