【问题标题】:Freeing memory in C在 C 中释放内存
【发布时间】:2009-04-15 17:46:24
【问题描述】:

这个小程序有问题:

已更新(根据某些要求,我已将所有内容都包含在此处,以便明确我在做什么。抱歉,它太长了): Student.h 文件:

typedef struct Student {
  char *name;
  int age;
  char *major;
    char *toString;
} *Student;

extern Student newStudent(char *name, int age, char *major);

Student.c 文件: c

har *studentToString(Student s);
static void error(char *s) {
  fprintf(stderr,"%s:%d %s\n",__FILE__,__LINE__,s);
  exit(1);
}

extern Student newStudent(char *name, int age, char *major) {
  Student s;
    if (!(s=(Student)malloc(sizeof(*s)))){
    error("out of memory");
    }
  s->name=name;
  s->age=age;
  s->major=major;
    s->toString = studentToString(s);
  return s;
}

char *studentToString(Student s) {
  const int size=3;
  char age[size+1];
  snprintf(age,size,"%d",s->age);

  char *line=newString();
  line=catString(line,"<");
  line=catString(line,s->name);
  line=catString(line," ");
  line=catString(line,age);
  line=catString(line," ");
  line=catString(line,s->major);
  line=catString(line,">");
  return line;
}

Students.c 文件:

static void error(char *s) {
  fprintf(stderr,"%s:%d %s\n",__FILE__,__LINE__,s);
  exit(1);
}

static StudentList alloc(StudentList students, Student student) {

  StudentList p;
    if (!(p=(StudentList)malloc(sizeof(*p)))){
    error("out of memory");}
  p->student=student;
  p->students=students;
  return p;
}

extern Students newStudents() {
  Students p;
    if (!(p=(Students)malloc(sizeof(*p)))){
    error("out of memory");
    }
  p->cursor=0;
  p->students=0;
  return p;
}

extern void addStudent(Students students, Student student) {
  StudentList p=students->students;
  if (!p) {
    students->students=alloc(0,student);

    return;
  }
while (p->students)
     p=p->students;
  p->students=alloc(0,student); 
    }    

extern void initStudent(Students students) {
  students->cursor=students->students;
}

extern Student currStudent(Students students) {
  StudentList cursor=students->cursor;
  if (!cursor)
    return 0;
  return cursor->student;
}

extern void nextStudent(Students students) {
  students->cursor=students->cursor->students;
}

还有我的主要方法:

int main() {
  Students students=newStudents();
  addStudent(students,newStudent("Julie",22,"CS"));
  addStudent(students,newStudent("Trevor",32,"EE"));

  for (initStudent(students);
       currStudent(students);
       nextStudent(students)) {
      char *line=currStudent(students)->toString;
    printf("%s\n",line);
      free(currStudent(students));
    free(line);
  }

    free(students->students);
    free(students);
  return 0;
}

我正在使用 valgrind 来检查内存泄漏,它弹出以下错误:

8 bytes in 1 blocks are definitely lost in loss record 1 of 1
==9520==    at 0x40054E5: malloc (vg_replace_malloc.c:149)
==9520==    by 0x8048908: alloc (Students.c:13)
==9520==    by 0x80489EB: addStudent (Students.c:42)
==9520==    by 0x804882E: main (StudentList.c:10)

我知道我需要释放 alloc 函数中为 p 分配的内存,但我应该在哪里调用 free(p)?还是我做错了什么?请帮忙!

【问题讨论】:

  • StudentList 是 Student* 的 typedef 吗?
  • 您还应该包含 main() 以跟踪泄漏
  • 我们相当确定 student 是一个简单的结构体。我们对 Student 和 StudentList 的定义更感兴趣。
  • 是的,不知道 Stundents 和 StudentList 有点令人困惑。
  • 隐藏指针的 typedef 很糟糕。至少命名它,以便有一些迹象表明它是一个指针。

标签: c pointers memory-leaks


【解决方案1】:

问题是,当您使用完 StudentStudentList 并且不再需要它时,您会做什么。这就是您应该为该结构中所有已分配的东西调用free() 的地方。

您可能需要一个freeStudents 函数,它遍历学生列表并释放其中的所有Students 和所有StudentList 项目。然后,只要您想删除学生列表,就调用该函数。

【讨论】:

    【解决方案2】:

    抱歉,这是切题,但您确实可以做很多事情来使您的代码更具可读性。

    您创建一个结构,然后将其类型重新定义为指向它的指针。哎呀,这只是在可维护性方面自找麻烦。隐藏指针就是指针的事实通常是个坏主意,因为当人们看到这样的东西时:

    Students newStudents()
    {
      Students p;
      // ...
      return p;
    }
    

    约定迫使我们假设您正在返回一个分配在堆栈上的结构,这显然是不正确的。 (编辑:不一定是“明显不正确”,而是一种浪费的副本。)

    当你添加你的 malloc 时,事情变得更加复杂......

    Students p;
    if (!(p=(Students)malloc(sizeof(*p))))
    {
      error("out of memory");
    }
    

    一方面,如前所述,人们假设没有 *,Students 是堆栈上的完整结构。这将使任何看到“sizeof(*p)”的人都重复一遍。你在做什么并不明显。

    虽然将赋值和比较压缩到一个 if 语句中是完全有效的 C 和 C++,但它通常不是最易读的解决方案。大大改进:

    Students* newStudents ()
    {
      Students* p = (Students*) malloc (sizeof (Students));
      if (p == NULL)
      {
         // ...
      }
      // ...
      return p;
    }
    

    人们乐于指出,在 C 中强制转换 malloc 的返回值不是必需的,但在 C++ 中却是这样。

    至于你的泄漏,嗯...... valgrind 没有报告你的 catString 使用情况,但它仍然很粗略,因为你隐藏了内存使用情况。使用 snprintf 是一种更好、更惯用的方法来创建所需的字符串。

    valgrind 正在报告泄漏:看起来您只是在释放列表中的第一个“学生”节点。你需要遍历它并释放它们,大概是这样的:

    Students p = students;
    while (p)
    {
      Students next = p->students;
      free (p);
      p = next;
    }
    

    【讨论】:

      【解决方案3】:

      我认为你的分配有问题。

      如果你想要一个指针,它应该是

      StudentList *p;
      p = (StudentList*)malloc(sizeof(StudentList));
      

      【讨论】:

        【解决方案4】:

        如果我使用 malloc(),我从不将 sizeof 与变量一起使用。您应该使用malloc(n * sizeof(StudentList))。话虽如此...

        您有一些重大问题。首先,您没有告诉我们 Student 和 StudentList 的具体定义是什么。在某些情况下,当我认为你的意思是 NULL 时,你会传递 0——当你的意思是 NULL 时,永远不要使用 0。如果 malloc() 失败——意思是,它返回 NULL——那么你就不会在结果上调用 free()。永远,永远免费 NULL。

        您应该只释放 (a) 成功分配的内存块,以及 (b) 当您不再需要它时。这似乎没有在您的代码中输入。

        还有其他问题(当您初始化 p 时,您假设 addStudent 中的学生非 NULL),但它们并没有完全解决您关于 free() 使用的问题。

        【讨论】:

        • 我完全不同意第一条评论。如果使用变量名,如果要更改变量的类型,则无需修改 sizeof() 调用。
        • free(p) 在这种情况下不会做任何有用的事情,但释放 NULL 应该不会有一般问题...通常 free(NULL) 什么都不做。
        • Volte,他在 sizeof 中使用了 *p——我认为这是一种不好的形式。我认为如果要分配,您应该始终使用该类型。如果您更改变量的类型,必须更改 sizeof 在我看来是一件小事。
        • 主要不同意第一部分,关于不使用变量。我衷心推荐这种用法,它很棒。不要重复自己,等等。
        【解决方案5】:

        这是做什么的:

        if (!(p=(StudentList)malloc(sizeof(*p)))){
            free(p);
        

        那么,如果p不能被分配,释放它?

        【讨论】:

          【解决方案6】:

          我假设 StudentList 被定义为某种指向 Student 的指针。在这种情况下,你的 alloc 函数中的这一行是一个问题。 p=(StudentList)malloc(sizeof(*p))

          您试图在指针被分配之前取消引用它,这是不好的。

          【讨论】:

          • 不,采用 sizeof(*p) 实际上并没有取消对 p 的引用。它相当于 sizeof(whatever_the_type_that_p_points_to)。
          • 详细地说,sizeof() 是一个编译时操作,因此在程序运行时 sizeof(whatever) 会被一个常数值替换。编译器知道 *p 是 StudentList。
          【解决方案7】:

          当您不再需要使用已分配的对象时,您应该释放内存。

          StudentListStudent 是什么?在我看来,您的数据结构构造不正确。为什么您的StudentList 存储指向另一个StudentList 的指针?您可能应该使用malloc 创建一个StudentList 并使用您的newStudents 函数返回指向它的指针,而不是返回一个带有addStudent 中旧结构副本的新StudentList 结构,只需修改您的结构最初创建的。

          所以它会是这样的:

          StudentList *newStudentList() {
              //malloc a new StudentList and return the pointer
          }
          void freeStudentList(StudentList *list) {
              //free the list pointer and all its child resources (i.e. the students in the list)
          }
          Student *newStudent(const char *name, etc) {
              //malloc a new Student and return the pointer
          }
          void addStudentToList(StudentList *list, Student *student) {
              //modify the StudentList which has been passed in by adding the student to it.
          }
          

          如果您不打算单独使用 newStudent 和 addStudent,您可以将它们合并到它们中,如果您还计划与 freeStudentList 分开做这些事情,您可能需要一个 freeStudent 和 removeStudentFromList 函数。您应该在 Google 上查看 C 中动态数据结构的示例(here 是 Google 上的第一个结果,但还有很多其他结果)。

          【讨论】:

            【解决方案8】:

            你真的需要定义你的类型是什么。您能否编辑您的帖子以包含 StudentStudentListStudents 的定义?

            还有,

            StudentList p;
            p = (StudentList) malloc(sizeof(*p);
            

            除非 StudentList 是指针的 typedef,否则我不确定它是如何编译的。

            我们也有这条线:

            StudentList p=students->students
            

            其中 p 被定义为 学生。那么 Students 也必须是指针的 typedef。


            另外,我认为最终是您的问题是,当您尝试在链接列表中插入学生时,您最终会丢失任何现有列表。您可以尝试将 3 个学生插入到列表中,然后尝试打印该列表。

            【讨论】:

              猜你喜欢
              • 2010-10-23
              • 1970-01-01
              • 2016-04-06
              • 1970-01-01
              • 2016-09-07
              • 1970-01-01
              • 1970-01-01
              • 2015-09-10
              • 2015-06-25
              相关资源
              最近更新 更多