*点动态数组在重新分配后崩溃和泄漏内存 - 为什么?
Dynamic array of *points crashing & leaking memory after reallocation - why?
编辑:澄清一下,我在 指针数组 之后,类型为 Point3d
结构。
我把看似简单的事情搞砸了。我在这里阅读了几篇关于动态分配数组的文章,但我仍然看不出哪里出错了。
在下面的代码中,我试图创建一个动态分配的 Point3d 结构指针数组。就这样。但是我弄乱了内存分配(valgrind 报告丢失了 40 个字节等)并且当不止一次重新分配数组时(ALLOC_COUNT > 1)它在重新分配时崩溃。我哪里做错了?
我只针对 C99。在 Windows 上执行此操作,在 WSL 上使用 valgrind 的 CLion。
完整程序:
#include <stdio.h>
#include <stdlib.h>
typedef struct Point3d_t {
double X;
double Y;
double Z;
} Point3d;
size_t increment_size = 0;
size_t vertex_count = 0;
size_t current_size = 0;
void *destructVertices(Point3d **vertices) {
if (vertices != NULL && ((current_size > 0) || (vertex_count > 0))) {
for (int i = 0; i < current_size; ++i) {
free(vertices[i]);
vertices[i] = NULL;
}
}
return NULL;
}
size_t allocateVertices(Point3d **vertices) {
const size_t new_size = current_size + 1 + increment_size;
Point3d **expanded_items = realloc(*vertices, new_size * sizeof(Point3d *));
if (!expanded_items) { // Allocation failed.
free(*vertices); fprintf(stderr, "RIL: Error reallocating lines array\n");
expanded_items = NULL; exit(0);
}
else if (expanded_items == vertices) {
expanded_items = NULL; // Same mem addr, no need to change
}
else {
vertices = expanded_items; // mem allocated to new address, point there
}
current_size = new_size;
return current_size;
}
/// returning the last item count, which can also be interpreted
/// as true (>0) or false (0) by the caller
size_t addVertex(Point3d point, Point3d **vertices) {
if (vertex_count == current_size) {
if (!allocateVertices(vertices)) return 0;
}
Point3d* p = malloc(sizeof(Point3d));
if (!p) exit(99);
p->X = point.X;
p->Y = point.Y;
p->Z = point.Z;
vertices[vertex_count] = p;
vertex_count += 1;
return vertex_count; // last item count
}
Point3d *m_vertices[1];
将 ALLOC_COUNT 增加到大于 1 的值(导致重新分配)将在释放时崩溃。
#define PT_COUNT 4
#define ALLOC_COUNT 1
int main() {
increment_size = PT_COUNT;
current_size = 0;
vertex_count = 0;
printf("Size of Point3d = %zu\n", sizeof(Point3d)); // fake error
printf("Size of *Point3d = %zu\n", sizeof(Point3d *)); // fake error
printf("Size of **m_vertices = %zu\n", sizeof(m_vertices)); // fake error
printf("Size of *m_vertices = %zu\n", sizeof(*m_vertices)); // fake error
printf("Size of *m_vertices[0] = %zu\n", sizeof(*m_vertices[0])); // fake error
printf("---------------------------\n"); // fake error
m_vertices[0] = NULL; //malloc(sizeof(Point3d*));
// new points
for (int i = 0; i < (PT_COUNT * ALLOC_COUNT); ++i) {
Point3d p;
p.X = 1.0 + (i * 0.001);
p.Y = 2.0 + (i * 0.001);
p.Z = 3.0 + (i * 0.001);
const size_t n = addVertex(p, m_vertices);
if (!n) exit(1);
}
printf("vertices.Capacity = %zu\n", current_size);
printf("vertices.Count = %zu\n", vertex_count);
destructVertices(m_vertices);
//free(*m_vertices);
return 0;
}
最终代码(省略了一些fwd声明和函数原型)。
如前所述,目标是管理 指针 的动态数组,而不会泄漏。使用 Valgrind 进行测试(尽管我实际上将 (void*)NULL
分配给了新(重新)分配的 space,但它只抱怨 realloc
导致的未初始化 space,但无论如何)。
省略了一些函数指针原型(fn_...
)。代码风格很奇怪,但我需要从一些现有的 C# 原型中粘贴大量代码,所以我部分使用 C# 风格,以便更容易双向复制粘贴。
typedef struct Point3d_t Point3d;
typedef struct Point3d_t
double X;
double Y;
double Z;
} Point3d;
// This "list owner" was omitted in the OP.
typedef struct MeshVertexList_t {
size_t AllocationsCount;
size_t Capacity;
size_t Count;
size_t Increment;
f_Add Add;
f_allocateVertices allocateVertices;
f_Destroy Destroy;
Point3d **Items; // array of ptr
} MeshVertexList;
MeshVertexList *New_MeshVertexList(size_t increment) {
MeshVertexList *mvlist = malloc(sizeof(MeshVertexList));
mvlist->AllocationsCount = 0;
mvlist->Capacity = 0;
mvlist->Count = 0;
mvlist->Increment = increment;
mvlist->Add = Add;
mvlist->Destroy = Destruct_MeshVertexList;
mvlist->allocateVertices = allocateVertices;
mvlist->Items = NULL;
return mvlist;
}
// Assigned to function pointer
size_t Add(MeshVertexList *self, Point3d *point) {
if (self->Count == self->Capacity) {
int res = (int) allocateVertices(self);
if (!res) return 0;
}
self->Items[self->Count] = point;
self->Count++;
return self->Count; // last item count
}
// Assigned to function pointer
size_t allocateVertices(MeshVertexList *Vertices) {
size_t new_capacity = 1;
if (Vertices->Capacity > 0) new_capacity = Vertices->Capacity + Vertices->Increment;
void *items_tmp = realloc(Vertices->Items, sizeof(*Vertices->Items) + new_capacity * sizeof(Point3d*));
if (!items_tmp) {
Destruct_MeshVertexList(Vertices);
perror("RIL: Error reallocating lines array\n");
exit(EXIT_ALLOC_ERROR);
} else if (items_tmp == Vertices->Items) {
items_tmp = NULL; // Same mem addr, no need to change
} else {
Vertices->Items = items_tmp;
}
for (int i = Vertices->Capacity; i < new_capacity; ++i) {
Vertices->Items[i] = (void *)NULL; // Valgrind doesn't detect this!
}
Vertices->AllocationsCount++;
Vertices->Capacity = new_capacity;
return Vertices->Capacity;
}
// Assigned to function pointer
void Destruct_MeshVertexList(MeshVertexList *Vertices) {
for (int i = 0; i <= Vertices->Capacity; ++i) {
if (Vertices->Items[i] != NULL) {
free(Vertices->Items[i]); // deallocating point
Vertices->Items[i] = NULL;
}
}
Vertices->Capacity = 0;
Vertices->Count = 0;
Vertices->Increment = 0;
Vertices->Add = NULL;
Vertices->allocateVertices = NULL;
free(Vertices->Items);
Vertices->Items = NULL;
free(Vertices);
Vertices = NULL;
}
typedef struct MeshObj_t {
MeshVertexList *Vertices;
} MeshObj;
#define ALLOC_INCREMENT 1 // <-- debug: realloc for each added pt
#define PT_COUNT 10
int main() {
const clock_t start = clock();
MeshObj m;
m.Vertices = New_MeshVertexList(ALLOC_INCREMENT);
printf("Size of Point3d = %zu\n", sizeof(Point3d));
printf("Size of *Point3d = %zu\n", sizeof(Point3d *));
printf("Size of m_vertices = %zu\n", sizeof(m.Vertices));
//printf("Size of *m_vertices[0] = %zu\n", sizeof(*m_vertices[0]));
printf("---------------------------\n");
for (int i = 0; i < (PT_COUNT); ++i) {
Point3d *p = malloc(sizeof(Point3d));
p->X = 1.0 + (i * 0.001);
p->Y = 2.0 + (i * 0.001);
p->Z = 3.0 + (i * 0.001);
size_t n = m.Vertices->Add(m.Vertices, p);
p = NULL;
if (!n) exit(1);
}
const clock_t stop = clock();
const double elapsed_time = (double) (stop - start) / CLOCKS_PER_SEC;
printf("Vertices->Capacity : %zu\n", m.Vertices->Capacity);
printf("Vertices->Count : %zu\n", m.Vertices->Count);
printf("Elapsed time : %lf ms\n", elapsed_time);
printf("---------------------------\n");
for (int i = 0; i < m.Vertices->Count; ++i) {
printf("%f %f %f\n", m.Vertices->Items[i]->X, m.Vertices->Items[i]->Y, m.Vertices->Items[i]->Z);
}
m.Vertices->Destroy(m.Vertices);
m.Vertices = NULL;
return 0;
}
编辑:澄清一下,我在 指针数组 之后,类型为 Point3d
结构。
我把看似简单的事情搞砸了。我在这里阅读了几篇关于动态分配数组的文章,但我仍然看不出哪里出错了。
在下面的代码中,我试图创建一个动态分配的 Point3d 结构指针数组。就这样。但是我弄乱了内存分配(valgrind 报告丢失了 40 个字节等)并且当不止一次重新分配数组时(ALLOC_COUNT > 1)它在重新分配时崩溃。我哪里做错了?
我只针对 C99。在 Windows 上执行此操作,在 WSL 上使用 valgrind 的 CLion。
完整程序:
#include <stdio.h>
#include <stdlib.h>
typedef struct Point3d_t {
double X;
double Y;
double Z;
} Point3d;
size_t increment_size = 0;
size_t vertex_count = 0;
size_t current_size = 0;
void *destructVertices(Point3d **vertices) {
if (vertices != NULL && ((current_size > 0) || (vertex_count > 0))) {
for (int i = 0; i < current_size; ++i) {
free(vertices[i]);
vertices[i] = NULL;
}
}
return NULL;
}
size_t allocateVertices(Point3d **vertices) {
const size_t new_size = current_size + 1 + increment_size;
Point3d **expanded_items = realloc(*vertices, new_size * sizeof(Point3d *));
if (!expanded_items) { // Allocation failed.
free(*vertices); fprintf(stderr, "RIL: Error reallocating lines array\n");
expanded_items = NULL; exit(0);
}
else if (expanded_items == vertices) {
expanded_items = NULL; // Same mem addr, no need to change
}
else {
vertices = expanded_items; // mem allocated to new address, point there
}
current_size = new_size;
return current_size;
}
/// returning the last item count, which can also be interpreted
/// as true (>0) or false (0) by the caller
size_t addVertex(Point3d point, Point3d **vertices) {
if (vertex_count == current_size) {
if (!allocateVertices(vertices)) return 0;
}
Point3d* p = malloc(sizeof(Point3d));
if (!p) exit(99);
p->X = point.X;
p->Y = point.Y;
p->Z = point.Z;
vertices[vertex_count] = p;
vertex_count += 1;
return vertex_count; // last item count
}
Point3d *m_vertices[1];
将 ALLOC_COUNT 增加到大于 1 的值(导致重新分配)将在释放时崩溃。
#define PT_COUNT 4
#define ALLOC_COUNT 1
int main() {
increment_size = PT_COUNT;
current_size = 0;
vertex_count = 0;
printf("Size of Point3d = %zu\n", sizeof(Point3d)); // fake error
printf("Size of *Point3d = %zu\n", sizeof(Point3d *)); // fake error
printf("Size of **m_vertices = %zu\n", sizeof(m_vertices)); // fake error
printf("Size of *m_vertices = %zu\n", sizeof(*m_vertices)); // fake error
printf("Size of *m_vertices[0] = %zu\n", sizeof(*m_vertices[0])); // fake error
printf("---------------------------\n"); // fake error
m_vertices[0] = NULL; //malloc(sizeof(Point3d*));
// new points
for (int i = 0; i < (PT_COUNT * ALLOC_COUNT); ++i) {
Point3d p;
p.X = 1.0 + (i * 0.001);
p.Y = 2.0 + (i * 0.001);
p.Z = 3.0 + (i * 0.001);
const size_t n = addVertex(p, m_vertices);
if (!n) exit(1);
}
printf("vertices.Capacity = %zu\n", current_size);
printf("vertices.Count = %zu\n", vertex_count);
destructVertices(m_vertices);
//free(*m_vertices);
return 0;
}
最终代码(省略了一些fwd声明和函数原型)。
如前所述,目标是管理 指针 的动态数组,而不会泄漏。使用 Valgrind 进行测试(尽管我实际上将 (void*)NULL
分配给了新(重新)分配的 space,但它只抱怨 realloc
导致的未初始化 space,但无论如何)。
省略了一些函数指针原型(fn_...
)。代码风格很奇怪,但我需要从一些现有的 C# 原型中粘贴大量代码,所以我部分使用 C# 风格,以便更容易双向复制粘贴。
typedef struct Point3d_t Point3d;
typedef struct Point3d_t
double X;
double Y;
double Z;
} Point3d;
// This "list owner" was omitted in the OP.
typedef struct MeshVertexList_t {
size_t AllocationsCount;
size_t Capacity;
size_t Count;
size_t Increment;
f_Add Add;
f_allocateVertices allocateVertices;
f_Destroy Destroy;
Point3d **Items; // array of ptr
} MeshVertexList;
MeshVertexList *New_MeshVertexList(size_t increment) {
MeshVertexList *mvlist = malloc(sizeof(MeshVertexList));
mvlist->AllocationsCount = 0;
mvlist->Capacity = 0;
mvlist->Count = 0;
mvlist->Increment = increment;
mvlist->Add = Add;
mvlist->Destroy = Destruct_MeshVertexList;
mvlist->allocateVertices = allocateVertices;
mvlist->Items = NULL;
return mvlist;
}
// Assigned to function pointer
size_t Add(MeshVertexList *self, Point3d *point) {
if (self->Count == self->Capacity) {
int res = (int) allocateVertices(self);
if (!res) return 0;
}
self->Items[self->Count] = point;
self->Count++;
return self->Count; // last item count
}
// Assigned to function pointer
size_t allocateVertices(MeshVertexList *Vertices) {
size_t new_capacity = 1;
if (Vertices->Capacity > 0) new_capacity = Vertices->Capacity + Vertices->Increment;
void *items_tmp = realloc(Vertices->Items, sizeof(*Vertices->Items) + new_capacity * sizeof(Point3d*));
if (!items_tmp) {
Destruct_MeshVertexList(Vertices);
perror("RIL: Error reallocating lines array\n");
exit(EXIT_ALLOC_ERROR);
} else if (items_tmp == Vertices->Items) {
items_tmp = NULL; // Same mem addr, no need to change
} else {
Vertices->Items = items_tmp;
}
for (int i = Vertices->Capacity; i < new_capacity; ++i) {
Vertices->Items[i] = (void *)NULL; // Valgrind doesn't detect this!
}
Vertices->AllocationsCount++;
Vertices->Capacity = new_capacity;
return Vertices->Capacity;
}
// Assigned to function pointer
void Destruct_MeshVertexList(MeshVertexList *Vertices) {
for (int i = 0; i <= Vertices->Capacity; ++i) {
if (Vertices->Items[i] != NULL) {
free(Vertices->Items[i]); // deallocating point
Vertices->Items[i] = NULL;
}
}
Vertices->Capacity = 0;
Vertices->Count = 0;
Vertices->Increment = 0;
Vertices->Add = NULL;
Vertices->allocateVertices = NULL;
free(Vertices->Items);
Vertices->Items = NULL;
free(Vertices);
Vertices = NULL;
}
typedef struct MeshObj_t {
MeshVertexList *Vertices;
} MeshObj;
#define ALLOC_INCREMENT 1 // <-- debug: realloc for each added pt
#define PT_COUNT 10
int main() {
const clock_t start = clock();
MeshObj m;
m.Vertices = New_MeshVertexList(ALLOC_INCREMENT);
printf("Size of Point3d = %zu\n", sizeof(Point3d));
printf("Size of *Point3d = %zu\n", sizeof(Point3d *));
printf("Size of m_vertices = %zu\n", sizeof(m.Vertices));
//printf("Size of *m_vertices[0] = %zu\n", sizeof(*m_vertices[0]));
printf("---------------------------\n");
for (int i = 0; i < (PT_COUNT); ++i) {
Point3d *p = malloc(sizeof(Point3d));
p->X = 1.0 + (i * 0.001);
p->Y = 2.0 + (i * 0.001);
p->Z = 3.0 + (i * 0.001);
size_t n = m.Vertices->Add(m.Vertices, p);
p = NULL;
if (!n) exit(1);
}
const clock_t stop = clock();
const double elapsed_time = (double) (stop - start) / CLOCKS_PER_SEC;
printf("Vertices->Capacity : %zu\n", m.Vertices->Capacity);
printf("Vertices->Count : %zu\n", m.Vertices->Count);
printf("Elapsed time : %lf ms\n", elapsed_time);
printf("---------------------------\n");
for (int i = 0; i < m.Vertices->Count; ++i) {
printf("%f %f %f\n", m.Vertices->Items[i]->X, m.Vertices->Items[i]->Y, m.Vertices->Items[i]->Z);
}
m.Vertices->Destroy(m.Vertices);
m.Vertices = NULL;
return 0;
}