C: правильно освобождает память многомерного массива - PullRequest
37 голосов
/ 14 ноября 2009

Скажем, у вас есть следующий код 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, как правильно освободить его из памяти?


[Обновление] (Решение)

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

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

Ответы [ 5 ]

65 голосов
/ 14 ноября 2009

ОК, есть большая путаница, объясняющая, в каком именно порядке необходимо 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 в первой приведенной выше строке и все остальное автоматически встанет на свое место для вас. Это исключает вероятность незаметных ошибок в будущем.

Удачи!

8 голосов
/ 14 ноября 2009

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

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

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

4 голосов
/ 14 ноября 2009

Вам нужно еще раз выполнить итерацию массива и выполнить столько освобождений, сколько malloc для указанной памяти, а затем освободить массив указателей.

for (i = 0; i < m; i++) { 
      free (a[i]);
}
free (a);
3 голосов
/ 14 ноября 2009

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

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

  free(a);

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

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

1 голос
/ 14 ноября 2009

Я бы вызвал 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;
}
...