C - 释放动态分配的阵列后出现分段错误



我正在尝试创建动态分配的集群数组,其中每个集群都有自己的动态分配点数组。

typedef struct Point
{
int ID; //each point has some ID and XY coordinates
int X;
int Y;
}Point;
typedef struct Cluster
{
int size;      //how many points can cluster hold
int count;     //how many points cluster actually holds
Point *points; //dynamically allocated array of points
}Cluster;

我能够初始化集群数组并用这个函数填充它:

void fill_array(Cluster **arr, int how_many_clusters)
{
*arr = (Cluster *)malloc(sizeof(Cluster) * how_many_clusters);
for(int i = 0; i < how_many_clusters; i++)
{
Point point;
point.ID = i;
point.X = i * 2;
point.Y = i * 3;
(*arr)[i].size = 1;
(*arr)[i].count = 1;
(*arr)[i].points = (Point *)malloc(sizeof(Point));
(*arr)[i].points[i] = point;
}
}

总的来说,我想创建数组,用前面提到的函数填充它,然后释放它。

int main ()
{
Cluster *clusters;
int clusters_count = 20;
fill_array(&cluters, clusters_count);
for(int i = clusters_count - 1; i >= 0; i--)
{
free(clusters[i].points); //crash point
clusters[i].points = NULL;
}
free(clusters);
return 0;
}

当我编译并运行这个程序时,它在free的第二次迭代中崩溃,并出现分段错误。我花了过去两天的时间在互联网上挖掘,但没有找到任何关于我做错了什么。 谁能指出我看不到的错误在哪里?

没有必要投malloc的回归,这是不必要的。请参阅:我是否投射了 malloc 的结果?malloc返回void *与任何指针兼容。演员只能起到搅浑水的作用。只需分配指针points,然后在取消引用的指针上调用sizeof,例如:

(*arr)[i].points = malloc (sizeof *(*arr)[i].points);

虽然分配的正确语句,但它完全无法验证分配是否成功,而是检查malloc的回报是否NULL,例如

if (!((*arr)[i].points = malloc (sizeof *(*arr)[i].points))) {
perror ("malloc failed - points");
exit (EXIT_FAILURE);
}

*arr的分配也是如此,例如

if (!(*arr = malloc (sizeof **arr * nclusters))) {
perror ("malloc failed - nclusters");
exit (EXIT_FAILURE);
}

(注:how_many_clusters缩短为nclusters,我们不是在写小说)

您的主要问题是:

(*arr)[i].points[i] = point;

您只为一个Point结构分配内存,因此points[i]根本没有意义。你分配了sizeof(Point)- 你认为

有多少?由于您的目标是将point复制到指针(*arr)[i].points持有的地址处新分配的内存块,">您如何访问(或设置)指针引用的值(例如指向)?(答案:取消引用指针以获取值)。

这就是您在这里需要做的所有事情。没有附加在末尾的额外[..]来取消引用(*arr)[i].points,只需用一元'*'取消引用它,例如

*(*arr)[i].points = point;  /* copy struct to allocated memory */

把这些部分放在一起,你可以做如下的事情:

#include <stdio.h>
#include <stdlib.h>
typedef struct {
int ID; //each point has some ID and XY coordinates
int X;
int Y;
} Point;
typedef struct {
int size;      //how many points can cluster hold
int count;     //how many points cluster actually holds
Point *points; //dynamically allocated array of points
} Cluster;
void fill_array (Cluster **arr, int nclusters)
{
if (!(*arr = malloc (sizeof **arr * nclusters))) {
perror ("malloc failed - nclusters");
exit (EXIT_FAILURE);
}
for (int i = 0; i < nclusters; i++)
{
Point point;
point.ID = i;
point.X = i * 2;
point.Y = i * 3;
(*arr)[i].size = 1;
(*arr)[i].count = 1;
if (!((*arr)[i].points = malloc (sizeof *(*arr)[i].points))) {
perror ("malloc failed - points");
exit (EXIT_FAILURE);
}
*(*arr)[i].points = point;  /* copy struct to allocated memory */
}
}
int main (void) {
Cluster *clusters;
int clusters_count = 20;
fill_array (&clusters, clusters_count);
for(int i = 0; i < clusters_count; i++)
{
free(clusters[i].points); //crash point
clusters[i].points = NULL;
}
free (clusters);
return 0;
}

(注意:不必以相反的顺序free每个clusters[i].points。(这没有错,只是没有必要特意去做)。只需在循环中工作时释放它们即可。

示例使用/验证

在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您有 2个责任:(1)始终保留指向内存块起始地址的指针,以便 (2) 当不再需要内存块时可以释放它。

必须使用内存错误检查程序来确保不会尝试访问内存或超出/超出分配块的范围写入,不会尝试读取或基于未初始化值的条件跳转,最后确认释放了已分配的所有内存。

对于Linux来说,valgrind是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。

$ valgrind ./bin/fillclusters_alloc
==26217== Memcheck, a memory error detector
==26217== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==26217== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==26217== Command: ./bin/fillclusters_alloc
==26217==
==26217==
==26217== HEAP SUMMARY:
==26217==     in use at exit: 0 bytes in 0 blocks
==26217==   total heap usage: 21 allocs, 21 frees, 560 bytes allocated
==26217==
==26217== All heap blocks were freed -- no leaks are possible
==26217==
==26217== For counts of detected and suppressed errors, rerun with: -v
==26217== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认已释放已分配的所有内存,并且没有内存错误。

仔细查看,如果您有其他问题,请告诉我。

(*arr)[i].points[i] = point;

这看起来不对。它应该是

(*arr)[i].points[0] = point;

以下位:

(*arr)[i].points = (Point *)malloc(sizeof(Point));
(*arr)[i].points[i] = point;

未分配正确的内存量。i将是大于零的数字,但您只为一个Point分配了空间。你正在砸你的堆,崩溃就会随之而来。

最新更新