我正在尝试创建动态分配的集群数组,其中每个集群都有自己的动态分配点数组。
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
分配了空间。你正在砸你的堆,崩溃就会随之而来。