简体   繁体   English

如何在C中释放双指针数组?

[英]How to free double pointer array in C?

I am trying to write a function to deallocate/free a double pointer in C. I have a struct that has an allocated char, and a double pointer to another struct I have.我正在尝试编写一个函数来释放/释放 C 中的双指针。我有一个具有已分配字符的结构,以及一个指向我拥有的另一个结构的双指针。 I need to create a function free of that memory.我需要创建一个没有该内存的函数。 The structs I have:我拥有的结构:

typedef struct {
    char *name; // allocated
    int age;
    int height;
} student;

typedef struct {
    char *name; // allocated
    char *location; // allocated
    double area;
    int population;
    student **students; // fully allocated, NOT a reference array
} school;

I need to dispose the school function, I do have a function already but I'm not sure it's right我需要处理学校功能,我已经有一个功能但我不确定它是否正确


void dispose_school(school *r)
{

    free(r->name);
    free(r->location);
    free(r->students[i]);
    free(r);
}

If someone can shine some light, thank you!如果有人可以发光,谢谢!

You will have to iterate through all the elements of students , free them.您将不得不遍历students的所有元素,释放它们。 And then after your loop completes, free students as well.然后在你的循环完成后,免费的students也是如此。 See this code-请参阅此代码-

for(int i = 0;i < r->population;i++) {
    free(r->students[i]->name); // do this if your students[i]->name is dynamically allocated
    free(r->students[i]);
}

free(r->students);

Freeing the double pointer is straightforward.释放双指针很简单。 You have the pointers you allocate to hold each student , you have the struct you allocate to hold each students values ( name , age , height ), and finally you have the name you allocated storage for to hold the student's name.你有你分配来保存每个student指针,你有你分配来保存每个学生值的结构( nameageheight ),最后你有你分配存储的name来保存学生的名字。 This specifies the order in which you would have needed to allocate the memory.这指定了分配内存所需的顺序。

To free() a complex set of allocations, you simply take the order in reverse .free()一组复杂的分配,您只需将顺序颠倒过来 Here you have some number of students (the population ), so you would loop school->population times freeing:在这里,您有一些学生( population ),因此您将循环school->population时间释放:

  • the block of memory allocated to hold the students name ;分配用于保存学生name的内存块;
  • the block of memory for the student struct holding the name, age and height;保存姓名、年龄和身高的学生结构的内存块; and finally最后

After the loop completes, then you free all of the pointers students .循环完成后,您将释放所有指针students

That will free all the memory you have allocated for students , but you are not done.这将释放您为students分配的所有内存,但您还没有完成。 You will have also allocated storage for the school name , location and perhaps the school struct itself (unless you declared the base school struct with automatic-storage-duration ).您还将为学校namelocation以及学校结构本身分配存储空间(除非您使用自动存储持续时间声明了基础学校结构)。

Presuming you have allocated for the school struct and the rest of the pointers, you can write a free function that takes a pointer to the school and free all memory with something similar to the following (assuming you have used school->population as the student counter):假设您已为 school 结构和其余指针分配,您可以编写一个 free 函数,该函数接受指向 school 的指针并使用类似于以下内容释放所有内存(假设您已使用school->population作为学生柜台):

void destroy_school (school *s)
{
  /* loop over each student and free storage for (1) name and (2) struct */
  while (s->population--) {
    free (s->students[s->population]->name);
    free (s->students[s->population]);
  }
  
  /* free storage for student pointers */
  free (s->students);
  
  /* free school name, location and school struct */
  free (s->name);
  free (s->location);
  free (s);               /* remove if s has automatic-storage-duration */
}

A Short Example一个简短的例子

Putting the pieces together to allocate and free() all the needed information, you can write a short program to do so.将这些部分放在一起以分配和free()所有需要的信息,您可以编写一个简短的程序来执行此操作。 Splitting the code into functions to create_school() , add_student() , print_school() and destroy_school() keeps the logic straight.将代码拆分为函数create_school()add_student()print_school()destroy_school()保持逻辑清晰。 Using the population as your student counter, you could do something similar to:使用population作为你的学生计数器,你可以做类似的事情:

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

typedef struct {
    char *name; // allocated
    int age;
    int height;
} student;

typedef struct {
    char *name; // allocated
    char *location; // allocated
    double area;
    int population;
    student **students; // fully allocated, NOT a reference array
} school;

school *create_school (const char *name, const char *loc, double area)
{
  school *s = NULL;                   /* create school ptr init NULL */
  size_t len = strlen (name);         /* get lenght of name parameter */
  
  if (!(s = malloc (sizeof *s))) {    /* allocate/VALIDATE struct school */
    perror ("malloc-s");              /* handle error on allocation fail */
    return NULL;                      /* return failure */
  }
  
  if (!(s->name = malloc (len + 1))) {  /* allocate/VALIDATE for name */
    perror ("malloc-s.name");           /* handle error on allocation fail */
    free (s);                           /* clean up previous allocations */
    return NULL;
  }
  memcpy (s->name, name, len + 1);      /* copy name to s->name */
  
  len = strlen (loc);
  
  if (!(s->location = malloc (len + 1))) {  /* allocate/VALIDATE for loc */
    perror ("malloc-s.loc");                /* handle error */
    free (s->name);                         /* clean up prior allocations */
    free (s);
    return NULL;
  }
  memcpy (s->location, loc, len + 1);   /* copy location to school */
  
  s->area = area;                       /* set area and pop */
  s->population = 0;
  
  s->students = NULL;                   /* init students ptr NULL */
  
  return s;                             /* return allocated school struct */
}

int add_student (school *s, const char *name, int age, int height)
{
  size_t len = strlen (name);         /* length of name */
  void *tmp;
  
  /* reallocate/VALIDATE pointer for next student
   * ALWAYS realloc() using a temporary pointer so if realloc() fails
   * you don't overwrite your original pointer with NULL creating a 
   * memory leak.
   */
  if (!(tmp = realloc (s->students, (s->population + 1) * 
                                      sizeof *s->students))) {
    fprintf (stderr, "error: realloc student: %d\n", s->population);
    return 0;   
  }
  s->students = tmp;                  /* assign realloc'ed block to students */
  
  /* allocate/VALIDATE storage for struct student */
  if (!(s->students[s->population] = malloc (sizeof **s->students))) {
    perror ("malloc-s->students[s->population");
    return 0;
  }
  
  /* allocate/VALIDATE storage for student name */
  if (!(s->students[s->population]->name = malloc (len + 1))) {
    perror ("malloc-s->students[population]->name");
    return 0;
  } /* copy name to allocated space */
  memcpy (s->students[s->population]->name, name, len + 1);
  
  s->students[s->population]->age = age;         /* assign age and height */
  s->students[s->population]->height = height;
  s->population += 1;                            /* increment pop count */
  
  return 1;         /* return success */
}

void print_school (school *s)
{
  printf ("\nSchool     : %s\n"
          "Location   : %s\n"
          "Area       : %.2f\n"
          "N-Students : %d\n\n", 
          s->name, s->location, s->area, s->population);
  
  for (int i = 0; i < s->population; i++) {
    printf ("  %02d  %-16s  %4d  %4d\n", 
            i,
            s->students[i]->name, 
            s->students[i]->age, 
            s->students[i]->height);
  }
}

void destroy_school (school *s)
{
  /* loop over each student and free storage for (1) name and (2) struct */
  while (s->population--) {
    free (s->students[s->population]->name);
    free (s->students[s->population]);
  }
  
  /* free storage for student pointers */
  free (s->students);
  
  /* free school name, location and school struct */
  free (s->name);
  free (s->location);
  free (s);
}

int main (void) {
  
  /* allocate/initialize struct school, population == 0, students == NULL */
  school *RidgeMontHigh = create_school ("Ridgemont High", 
                                          "Hollywood, CA", 2357.8);
  
  if (!RidgeMontHigh) {   /* validate creation of school */
    return 1;
  }
  
  /* add students to school (can validate each, but population will do) */
  add_student (RidgeMontHigh, "Mickey Mouse", 103, 48);
  add_student (RidgeMontHigh, "Minnie Mouse",  99, 44);
  add_student (RidgeMontHigh, "Pluto (the dog)", 97, 47);
  add_student (RidgeMontHigh, "Daffy Duck", 102, 46);
  
  if (RidgeMontHigh->population == 0) {   /* validate non-zero student pop */
    return 1;
  }
  
  print_school (RidgeMontHigh);       /* print school followed by students */
  destroy_school (RidgeMontHigh);     /* free all memory involved */
}

Example Use/Output示例使用/输出

Compiling and running the code will produce the following:编译并运行代码将产生以下结果:

./bin/schoolpop

School     : Ridgemont High
Location   : Hollywood, CA
Area       : 2357.80
N-Students : 4

  00  Mickey Mouse       103    48
  01  Minnie Mouse        99    44
  02  Pluto (the dog)     97    47
  03  Daffy Duck         102    46

Memory Use/Error Check内存使用/错误检查

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您有 2 个责任:(1)始终保留指向内存块起始地址的指针,(2)它可以在它不存在时被释放更需要。

It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.您必须使用内存错误检查程序,以确保您不会尝试访问内存或写入超出/超出分配块的范围,尝试读取或基于未初始化值的条件跳转,最后确认释放所有分配的内存。

For Linux valgrind is the normal choice.对于 Linux valgrind是正常的选择。 There are similar memory checkers for every platform.每个平台都有类似的内存检查器。 They are all simple to use, just run your program through it.它们都易于使用,只需通过它运行您的程序即可。

$ valgrind ./bin/schoolpop
==18986== Memcheck, a memory error detector
==18986== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==18986== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==18986== Command: ./bin/schoolpop
==18986==

School     : Ridgemont High
Location   : Hollywood, CA
Area       : 2357.80
N-Students : 4

  00  Mickey Mouse       103    48
  01  Minnie Mouse        99    44
  02  Pluto (the dog)     97    47
  03  Daffy Duck         102    46
==18986==
==18986== HEAP SUMMARY:
==18986==     in use at exit: 0 bytes in 0 blocks
==18986==   total heap usage: 16 allocs, 16 frees, 1,290 bytes allocated
==18986==
==18986== All heap blocks were freed -- no leaks are possible
==18986==
==18986== For lists of detected and suppressed errors, rerun with: -s
==18986== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Always confirm that you have freed all memory you have allocated and that there are no memory errors.始终确认您已释放所有已分配的内存并且没有内存错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM