【问题标题】:Dynamic array of *points crashing & leaking memory after reallocation - why?*点的动态数组在重新分配后崩溃和泄漏内存 - 为什么?
【发布时间】:2021-01-13 10:43:27
【问题描述】:

编辑:澄清一下,我在一个 指针数组之后,类型为 Point3d 结构。

我搞砸了一些看起来很简单的事情。我在这里阅读了几篇关于动态分配数组的帖子,但我仍然看不出我哪里出错了。

在下面的代码中,我试图创建一个动态分配的 Point3d 结构指针数组。就这样。但是我搞砸了内存分配(valgrind 报告丢失了 40 个字节等),并且当多次重新分配数组时(ALLOC_COUNT > 1)它在释放时崩溃。我哪里做错了?

我只针对 C99。在 Windows 上执行此操作,CLion 在 WSL 上使用 valgrind。

完整的程序:

#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;
}

【问题讨论】:

    标签: c memory-leaks


    【解决方案1】:

    最终代码(省略了一些 fwd 声明和函数原型)。

    如前所述,目标是管理 指针 的动态数组,而不会出现泄漏。用 Valgrind 测试(它只抱怨realloc 导致的未初始化空间,尽管我实际上将(void*)NULL 分配给新(重新)分配的空间,但无论如何)。

    省略了一些函数指针原型 (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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-11
      • 1970-01-01
      • 2012-10-09
      • 1970-01-01
      • 1970-01-01
      • 2021-09-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多