复制结构的内容在临时结构为 `free` d 后发生变化

Contents of copied structure changes after temporary structure is `free` d

我正在制作一个简单的数据库程序来学习结构和二叉树。我创建了一个包含 3 个字段的全局结构 Student:名字、姓氏、年龄,并编写了一个函数来获取 3 个用户输入(存储为字符串 in_fnamein_sname 和 int in_age,比方说)并将它们放入临时结构 new,并计划将它们复制到主树的适当位置。在获取第一个条目的用户输入后,我有:

struct Student *new;
new = (Student *) malloc (sizeof(struct Student));
strcpy (new->fname, in_fname);
strcpy (new->sname, in_sname);
new->age = in_age;
new->left = new->right = NULL;
printf("Contents of new is '%s', '%s', '%d'.\n",new->fname, new->sname, new->age);

student_tree = new  /* wanting to copy the new student as the first entry in the tree*/

现在当我

print("Contents of structure is '%s', '%s', '%d'.\n",student_tree->fname, student_tree->sname, student_tree->age);

我得到了正确的条目,表明复制成功了,但是当我

free(new)
print("Contents of structure is '%s', '%s', '%d'.\n",student_tree->fname, student_tree->sname, student_tree->age);

(认为它是临时的,我不再需要它了)当我的第一个条目 fname 总是损坏,包含垃圾。

有人可以解释一下我缺少什么吗?不一定要固定的代码,只是为了理解为什么当我从中复制的东西消失时树中的结构内容会发生变化,以及如何永久复制它。

非常感谢,

W

因为student_treenew是指针。当你将new赋值给student_tree时,没有复制,它只是让student_tree指向同一个内存。当你调用 free(new) 时,它会回收 new 指向的内存,这也是 student_tree 指向的内存,所以它是垃圾是可以理解的。

下面是一些可以进行实际复制的代码:

struct Student* student_tree = malloc(sizeof(struct Student));
*student_tree = *new;

这里我创建了指针student_tree,分配内存来存放struct Student,最后将new指向的内存中的内容放到new指向的内存中student_tree.

执行 free(now) 然后稍后访问 now 就像关掉你的引擎,然后想知道为什么你的车在你踩下油门踏板时不再加速。

注意

student_tree = new

不是复制new指向的内容,只是赋值 另一个指向 new 指向的位置的指针。你这里有什么 只是指向同一位置的 2 个指针。如果你这样做 free(now),那么 两个指针都指向释放的内存,当然你不能访问 具有指针之一的内存(newstudent_tree)。

如果你想释放new,那么你必须复制内存。这个可以 像这样用 memcpy 完成:

struct Student copy;
memcpy(&copy, new, sizeof copy);

但取决于结构本身(是否包含指针或数组), memcpy 可能不是正确的解决方案。

现在很难给你一个正确的答案,因为太多了 您缺少的信息,例如 struct Person 的样子,如何插入 函数的外观、您如何称呼它们等等

还有

strcpy (new->fname, in_fname);
strcpy (new->sname, in_sname);

这可能很危险,您没有给我们足够的信息,但是如果您 一不小心,您可能会溢出缓冲区。我从你的代码中推测 fnamesname 都是 char 数组。在那种情况下,我会使用 strncpy 相反,因为可能不知道 in_fnamein_sname 的实际长度 and/or 可能比 fnamesname 可以容纳的更大。所以一般来说 更强大的解决方案是:

strncpy(new->fname, in_fname, sizeof new->fname);
new->fname[sizeof(new->fname) - 1] = '[=13=]';

strncpy(new->sname, in_sname, sizeof new->sname);
new->sname[sizeof(new->sname) - 1] = '[=13=]';

在您发布的代码中,new 是一个指针,不包含结构 Student,而是指向 Student 的内存地址。在 C 中,大部分数据可以存储在程序的堆栈内存或堆内存中。堆栈内存是受管理的,但在您退出范围时变得无效,而堆内存在整个程序中都是持久的。 malloc() 根据您的指示分配尽可能多的堆内存(在本例中,足够 Student)和 returns 指向已分配内存的指针。因为 new 指向的内存在整个程序中都是持久的,所以您必须使用 free() 手动释放内存。

这一行对于理解指针很重要:

student_tree = new;

在不知道 student_tree 的类型的情况下,很难说出发生了什么,但我们假设上面某处存在声明 struct Student *student_tree;。如果是这种情况,那么这两个变量都是在您的堆栈内存中分配的指针,并且 student_tree 被分配了 new 的值,这是您使用 malloc() 分配的内存地址。

因为两个变量现在都指向同一个内存,当内存被释放时,两个指针都没有指向有效内存。