Подтвердить что ты не робот

C: Правильное освобождение памяти многомерного массива

Скажем, у вас есть следующий код ANSI C, который инициализирует многомерный массив:

int main()
{
      int i, m = 5, n = 20;
      int **a = malloc(m * sizeof(int *));

      //Initialize the arrays
      for (i = 0; i < m; i++) { 
          a[i]=malloc(n * sizeof(int));
      }

      //...do something with arrays

      //How do I free the **a ?

      return 0;
}

После использования **a, как правильно освободить его из памяти?


[Обновить] (решение)

Благодаря Tim (и другим) answer, теперь я могу сделать такую ​​функцию, чтобы освободить память из моего многомерного массива:

void freeArray(int **a, int m) {
    int i;
    for (i = 0; i < m; ++i) {
        free(a[i]);
    }
    free(a);
}
4b9b3361

Ответ 1

Хорошо, там есть путаница, объясняющая, какой именно порядок необходимо, чтобы были вызовы free(), поэтому я попытаюсь уточнить, что люди пытаются понять и почему.

Начиная с основ, чтобы освободить память, которая была выделена используя malloc(), вы просто вызываете free() точно с указателем который вам дал malloc(). Итак, для этого кода:

int **a = malloc(m * sizeof(int *));

вам нужно сопоставить:

free(a);

и для этой строки:

a[i]=malloc(n * sizeof(int));

вам нужно сопоставить:

free(a[i]);

внутри аналогичного цикла.

Там, где это осложняется, это порядок, в котором это должно произойти. Если вы вызываете malloc() несколько раз, чтобы получить несколько разных кусков памяти, в общем случае не имеет значения, какой порядок вы называете free(), когда вы сделали с ними. Однако порядок здесь очень важен для конкретная причина: вы используете один кусок malloc ed памяти для хранения указатели на другие фрагменты памяти malloc ed. Потому что вы должны не пытайтесь читать или записывать память, как только вы передали ее free(), это означает, что вам нужно будет освободить куски с помощью их указатели хранятся в a[i], прежде чем вы освободите кусок a. Отдельные куски с указателями, хранящимися в a[i], не зависят от каждого другой, и поэтому может быть free d в любом порядке, который вам нравится.

Итак, положив все это вместе, получим следующее:

for (i = 0; i < m; i++) { 
  free(a[i]);
}
free(a);

Последний совет: при вызове malloc() рассмотрите возможность изменения:

int **a = malloc(m * sizeof(int *));

a[i]=malloc(n * sizeof(int));

в

int **a = malloc(m * sizeof(*a));

a[i]=malloc(n * sizeof(*(a[i])));

Что это делает? Компилятор знает, что a является int **, поэтому он может определите, что sizeof(*a) совпадает с sizeof(int *). Однако, если позже вы передумаете и хотите char или short или long или независимо от вашего массива вместо int s, или вы адаптируете этот код для последующего использовать в чем-то другом, вам придется изменить только оставшийся ссылку на int в первой цитируемой строке выше, и все остальное автоматически встанет на свои места. Это устраняет вероятность незаметных ошибок в будущем.

Удачи!

Ответ 2

Отмените именно то, что вы выделили:

  for (i = 0; i < m; i++) { 
      free(a[i]);
  }
  free(a);

Обратите внимание, что вы должны сделать это в обратном порядке, из которого вы первоначально выделили память. Если вы сначала выполнили free(a), тогда a[i] будет получать доступ к памяти после ее освобождения, что означает поведение undefined.

Ответ 3

Вам нужно снова повторить массив и сделать так много frees как mallocs для выделенной памяти, а затем освободить массив указателей.

for (i = 0; i < m; i++) { 
      free (a[i]);
}
free (a);

Ответ 4

Напишите ваши операторы распределения в точно противоположном порядке, изменив имена функций, и все будет в порядке.

  //Free the arrays
  for (i = m-1; i >= 0; i--) { 
      free(a[i]);
  }

  free(a);

Конечно, вам не нужно снимать в том же обратном порядке. Вам просто нужно следить за освобождением одной и той же памяти ровно один раз, а не "забывать" указатели на выделенную память (например, если бы вы сначала освободили a). Но освобождение в обратном порядке - хорошая роль большого пальца, чтобы обратиться к последнему.

Как указано litb в комментариях, если у распределения/освобождения были побочные эффекты (например, операторы new/delete в С++), иногда обратный порядок освобождения будет более важен, чем в этом конкретном примере.

Ответ 5

Я бы назвал malloc() и free() только один раз:

#include <stdlib.h>
#include <stdio.h> 

int main(void){
  int i, m = 5, n = 20;
  int **a = malloc( m*(sizeof(int*) + n*sizeof(int)) );

  //Initialize the arrays
  for( a[0]=(int*)a+m, i=1; i<m; i++ ) a[i]=a[i-1]+n;

  //...do something with arrays

  //How do I free the **a ?
  free(a);

  return 0;
}