Освободете динамично разпределената памет

В момента уча за указатели и разпределение на статична/динамична памет. В следния код имам 3D масив. Променливият масив е указател към указател към указател mat3***. Научих, че malloc разпределя памет от купчината и връща указател. Функцията free освобождава тази памет.

int main()
{
   double*** mat3;
   int m,n,p;
   int i,j,k;
   
   m=2; n=3; p=4; /* unequal dimensions should work fine as well */
   
   mat3 = (double***) malloc(m*sizeof(double**));
   if (mat3 == NULL)
    exit(1);
   
   for (i=0; i<m; i++)
   {
      mat3[i] = (double**) malloc(n*sizeof(double*));;
      if (mat3[i] == NULL)
          exit(1);
      
      for (j=0; j<n; j++)
      {
          mat3[i][j] = (double*) malloc(p*sizeof(double));
          if (mat3[i][j] == NULL)
             exit(1);
      }
   }
   
   // Fill with some data that makes checking easy (value = sum of the indexes) :
   for(i=0;i<m;i++)
      for(j=0;j<n;j++)
         for(k=0;k<p;k++)
            mat3[i][j][k] = i + 10*j + 100*k;
   
   show(mat3,m,n,p);
   
   free(mat3);
}

Така че след показването приключих с 3D масива и искам да освободя паметта. Затова използвам free, но наистина се съмнявам дали съм го направил правилно. Тъй като дадох команда за освобождаване на ***, но самият ** и * не бяха в команда. Така че това би означавало, че в купчината все още има указатели към указатели и самите указатели. Ако това е вярно, нямаше да знам как да освободя паметта, може би бих могъл да използвам същия цикъл, но тогава вместо да разпределя памет, бих могъл да я освободя.

Друг въпрос е по-скоро теоретичен. Тъй като освободих паметта, но паметта не се презаписва веднага. Ако използвам free() преди printf(), получавам грешка при сегментиране. Въпреки че паметта е освободена? Местоположението на физическата памет все още съществува, нали? Така че защо не можах да го осъществя с референтния адрес?


person Tim    schedule 10.10.2020    source източник
comment
free() освобождава, не преразпределя. Искате да освободите паметта.   -  person Marco Bonelli    schedule 10.10.2020
comment
Освен ако не искате разкъсан масив, не е необходимо да разпределяте всяко измерение отделно. Достатъчно е да маллокирате m * n * p записи и да индексирате в него с нещо като i * (m + n) + j * n + k...   -  person AKX    schedule 10.10.2020
comment
@AKX този формат е част от задание. Не мога да променя това.   -  person Tim    schedule 10.10.2020
comment
Добре е да malloc на измерение, но трябва също да free на измерение. free не е рекурсивен. Вижте също това.   -  person ggorlen    schedule 10.10.2020


Отговори (1)


Така че това би означавало, че в купчината все още има указатели към указатели и самите указатели.

Това е вярно. Като правило не забравяйте, че за всяко malloc() трябва да има съответстващо free(). Ти си:

  1. Разпределяне на място за 3D матрицата (m указатели от тип double **).
  2. Разпределяне на място за всяка 2D матрица (n указатели от тип double *, m*n общо).
  3. Разпределяне на място за всеки ред на всяка 2D матрица (p double стойности, m*n*p общо).

Следователно, когато се освободите, трябва да направите абсолютно същото, но обратно:

  1. Освободете паметта за всеки от редовете на всяка 2D матрица.
  2. Освободете паметта за всяка 2D матрица.
  3. Освободете паметта за 3D матрицата.

По отношение на кода:

for(i = 0; i < m; i++) {
    for(j = 0; j < n; j++) {
        free(mat3[i][j]);
    }

    free(mat3[i]);
}

free(mat3);

Тъй като освободих паметта, но паметта не се презаписва веднага. Ако използвам free() преди printf(), получавам грешка при сегментиране. Въпреки че паметта е освободена? Местоположението на физическата памет все още съществува, нали? Така че защо не можах да го осъществя с референтния адрес?

Не знаете нищо за това какво се случва с паметта, която освобождавате. Това, което наистина се случва, е специфично за изпълнението, специфично за операционната система и зависи от различни комплексни фактори. Може все още да съществува, може да е бил освободен от системата, може да е бил използван от различни части на програмата при извикване на други malloc() (printf() също използва malloc()). Достъпът до паметта след освобождаването й е недефинирано поведение.

person Marco Bonelli    schedule 10.10.2020
comment
Благодаря за отговора! Мислех за това отново и възможно ли е това без цикъл? Вместо цикъл бихте направили 3 оператора free(). free(mat3[0][0]) free(mat3[0]]) free(mat3) Тъй като [0][0] и [0] сочат към адреса на масива? - person Tim; 18.10.2020
comment
@Terr070 не, не е възможно без цикли. Трябва да освободите всички от тях, не само [0] или [0][0]. Правите 1 + n*m разпределения, трябва да направите 1 + n*m освобождавания. - person Marco Bonelli; 18.10.2020