C-指向结构组件(类型指针)的指针的重新分配已中止(内核已转储)

问题描述 投票:0回答:2

我想对图形进行编程,但是当我要添加顶点时会遇到问题。当我想重新分配内存时,程序停止,控制台仅显示“ aborted(核心已转储)”。

这是我的代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

struct Graph
{
    int VertexCounter;
    struct Vertices *vertex;
    struct Edge **adjMat;
}MyGraph;

struct Vertices
{
    int id;
    char name[15];
    float xPos;
    float yPos;
};

struct Edge
{
    int id;
    struct Vertices *start;
    struct Vertices *end;
};


//Initializing a graph with memory for one Vertex and one 1x1 adjecency Matrix but setting the number of Vertices to zero
void initGraph(struct Graph *graph)
{
    graph = (struct Graph *) calloc(1, sizeof(struct Graph));
    graph->vertex = (struct Vertices *) calloc(1, sizeof(struct Vertices));
    graph->adjMat = (struct Edge **) calloc(1, sizeof(struct Edge *));
    *(graph->adjMat) = (struct Edge *) calloc(1, sizeof(struct Edge));

    graph->VertexCounter = 0;
    printf("Number of Vertices: %d\n", graph->VertexCounter);

}

//printing the number of Vertices
void test(struct Graph *graph)
{
    printf("Number of Vertices: %d\n", (*graph).VertexCounter);
}


//Reallocating the memory for an additional Vertex. 
//I multiply the VertexCounter - 1 because the struct Graph contains space for one pointer of the type (struct Vertices *)
void addVertex(struct Graph *graph)
{
    graph->VertexCounter++;
    graph = (struct Graph *) realloc(graph, sizeof(struct Graph) + 
                            (graph->VertexCounter - 1) * sizeof(struct Vertices));

}


int main()
{
    struct Graph *graphPointer;
    graphPointer = &MyGraph;
    initGraph(graphPointer);
    test(graphPointer);
    addVertex(graphPointer);
    test(graphPointer);
    return 0;
}

输出:

顶点数:0

顶点数:0

中止(核心已弃用)

所以函数addVertex(struct Graph *)不起作用。

我还没有包括新Edge的重新分配。

我该如何解决?

更新:

void addVertex(struct Graph **graph)
{
    (*graph)->VertexCounter++;
    *graph = realloc(*graph, sizeof(struct Graph) + 
                ((*graph)->VertexCounter - 1) * sizeof(struct Vertices));

}


int main()
{
    struct Graph *graphPointer;
    graphPointer = &MyGraph;
    initGraph(graphPointer);
    test(graphPointer);
    addVertex(&graphPointer);
    test(graphPointer);
    return 0;
}

这将返回与以前相同的输出

更新2:

void initGraph(struct Graph **graph)
{
    (*graph) = (struct Graph *) calloc(1, sizeof(struct Graph *));
    (*graph)->vertex = (struct Vertices *) calloc(1, sizeof(struct Vertices));
    (*graph)->adjMat = (struct Edge **) calloc(1, sizeof(struct Edge *));
    *((*graph)->adjMat) = (struct Edge *) calloc(1, sizeof(struct Edge));

    (*graph)->VertexCounter = 0;
    printf("Number of Vertices: %d\n", (*graph)->VertexCounter);

}

//printing the number of Vertices
void test(struct Graph *graph)
{
    printf("Number of Vertices: %d\n", (*graph).VertexCounter);
}


//Reallocating the memory for an additional Vertex. 
//I multiply the VertexCounter - 1 because the struct Graph contains space for one pointer of the type (struct Vertices *)
void addVertex(struct Graph **graph)
{

    (*graph)->VertexCounter++;

    void *temp = realloc(temp, sizeof(struct Graph *));
    *graph = temp;

    if(graph == NULL)
        printf("Realloc failed");
}


int main()
{
    struct Graph *graphPointer;
    graphPointer = &MyGraph;
    initGraph(&graphPointer);
    test(graphPointer);
    addVertex(&graphPointer);
    test(graphPointer);
    return 0;
}

我更改了initGraph和addVertex,但输出不会更改。

c pointers memory struct allocation
2个回答
0
投票

如以上评论所指出,您需要跟踪更改时间,或者可以更改graphPointer。例如,每当您调用realloc时,系统可能会被迫移动您的结构,并返回指向新分配的新指针。再次指出,例如,在addVertex的末尾将丢弃此新指针。如果要更改函数中的指针,就像其他任何“按引用调用”参数一样,请将指针传递给该参数。 (如果您对类型完全严格,只有在确定需要进行类型转换时,编译器才能提供帮助。)

您真正需要做的另一件事是检查呼叫的返回值。有时您认为调用是有效的,但无效,并且由于参数错误而总是返回NULL。无论哪种方式,您都不希望您的程序在将来某个时候崩溃而没有任何原因的提示。因此,例如,如果重新分配失败,请至少打印该重新分配失败并退出。


0
投票

[好,我把您在示例中试图做的事情归结为一遍。 initGraph是您遇到的一个逻辑问题。您要在其中分配struct Graph,而不是struct Verticesstruct Edge **adjMat;。除非您要为其中一个添加值,否则只需将这些指针设置为NULL,直到您实际需要为其中一个分配时间为止。

首先,让我们重新排列您的struct定义,以便在需要其他结构之前就定义了另一个结构,例如

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

struct Vertices {
    int id;
    char name[15];
    float xPos;
    float yPos;
};

struct Edge {
    int id;
    struct Vertices *start;
    struct Vertices *end;
};

struct Graph {
    int VertexCounter;
    struct Vertices *vertex;
    struct Edge **adjMat;
};

(<< [note:您的MyGraph实例已被删除-如果您要分配graph,则没有必要]

让我们向前看,看看如何在main()中处理您的指针,例如

将更改的参数编辑为addVertex

注意,在以下所有代码中,addVertex的参数已从struct Graph **graph更改为struct Graph *graph,因为在graphpointer中没有重新分配addVertex

int main (void) { struct Graph *graphPointer = NULL; initGraph (&graphPointer); test(graphPointer); addVertex (graphPointer); test(graphPointer); freeGraph (graphPointer); /* don't forget to free what you allocate */ }

以上,graphpointer是唯一需要的指针。然后,将graphpointer的地址传递给initGraph,以便initGraph可以在原稿上进行操作,并且initGraph中的分配将在main()中可用。

要实现这一点,您可以在initGraph中执行以下操作(注意:struct Graph中对任何其他成员的分配都会延迟,直到您确实要添加一些东西为止)。如果您尝试预分配直到代码稍后出现的未知点才会发生的事情,那么您将创建灾难的秘诀。在需要时分配所需的内容。 (这不会阻止您为每个块分配一个块并保留一个计数器以减少所需的realloc数量)

您的initGraph然后变为:

void initGraph (struct Graph **graph) { *graph = calloc (1, sizeof **graph); if (!*graph) { /* validate EVERY allocation */ perror ("calloc-*graph"); exit (EXIT_FAILURE); } (*graph)->vertex = NULL; /* set pointers NULL until you add_vertex */ (*graph)->adjMat = NULL; /* or you add_adjmat */ (*graph)->VertexCounter = 0; /* initialize counter */ }

上面您已经初始化了一个struct Graph-仅此而已-应当如此。使用initGraph时,您需要传递graphpointer的地址,以便可以在main()中看到分配,而使用addVertex时,您只需要传递graphpointer-因为该指针的地址确实addVertex中不变。您要做的只是为struct Graph中的graphpointer成员分配,可以简单地将其分配给vertex指针。现在,当您准备添加顶点时,可以执行以下操作:

void addVertex (struct Graph *graph) { /* always realloc using a temporary pointer */ void *tmp = realloc (graph->vertex, (graph->VertexCounter + 1) * sizeof *graph->vertex); if (!tmp) { /* validate EVERY reallocation */ perror ("realloc-(*graph)->vertex"); exit (EXIT_FAILURE); } graph->vertex = tmp; graph->VertexCounter++; }

还请注意,addVertex中的所有操作都是struct Verticesgraphpointer成员-也应如此。如果此时要添加一个顶点,请考虑将其作为参数传递(或将数据作为参数传递),以便可以在addVertex中分配/复制该顶点数据,以将所有add-vertex代码保留在一个位置。

在您编写的任何可动态分配内存的代码中,对于任何已分配的内存块,您都有2个

职责:(1)始终保留指向起始地址的指针,因此,( 2)当不再需要它时,可以将其<< freed>。 freeGraph的快速free函数是到目前为止已分配的内容:void freeGraph (struct Graph *graph) { free (graph->vertex); free (graph); }

((添加adjMat的分配时,请不要忘记在free中添加相应的freeGraph
现在把它放在一起,您可以做:

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> struct Vertices { int id; char name[15]; float xPos; float yPos; }; struct Edge { int id; struct Vertices *start; struct Vertices *end; }; struct Graph { int VertexCounter; struct Vertices *vertex; struct Edge **adjMat; }; void initGraph (struct Graph **graph) { *graph = calloc (1, sizeof **graph); if (!*graph) { /* validate EVERY allocation */ perror ("calloc-*graph"); exit (EXIT_FAILURE); } (*graph)->vertex = NULL; /* set pointers NULL until you add_vertex */ (*graph)->adjMat = NULL; /* or you add_adjmat */ (*graph)->VertexCounter = 0; /* initialize counter */ } void test (struct Graph *graph) { printf("Number of Vertices: %d\n", graph->VertexCounter); } void addVertex (struct Graph *graph) { /* always realloc using a temporary pointer */ void *tmp = realloc (graph->vertex, (graph->VertexCounter + 1) * sizeof *graph->vertex); if (!tmp) { /* validate EVERY reallocation */ perror ("realloc-(*graph)->vertex"); exit (EXIT_FAILURE); } graph->vertex = tmp; graph->VertexCounter++; } void freeGraph (struct Graph *graph) { free (graph->vertex); free (graph); } int main (void) { struct Graph *graphPointer = NULL; initGraph (&graphPointer); test(graphPointer); addVertex (graphPointer); test(graphPointer); freeGraph (graphPointer); /* don't forget to free what you allocate */ } /* move to add_adjmat functions: (*graph)->adjMat = calloc (1, sizeof *(*graph)->adjMat); if (!(*graph)->adjMat) { perror ("calloc-(*graph)->adjMat"); exit (EXIT_FAILURE); } *(*graph)->adjMat = calloc (1, sizeof **(*graph)->adjMat); */

((

note:
adjMat分配给将要处理add_adjmat()的任何函数的底部的注释,等等。]

示例使用/输出

$ ./bin/graphalloc Number of Vertices: 0 Number of Vertices: 1

内存使用/错误检查

必须使用内存错误检查程序来确保您不尝试访问内存或不在分配的块的边界之外/之外写,尝试读取或基于未初始化的值进行条件跳转,最后,确认您释放了已分配的所有内存。

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

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

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

这是一种允许您在需要时添加所需内容的方法。请注意,为了最大程度地减少重新分配,您可以添加VertexAllocated成员并分配8, 16, 32, etc..的初始块,然后在VertexCounter == VertexAllocated时可以依次调用reallocrealloc 2次VertexAllocated并继续。

仔细检查,如果您有任何疑问,请告诉我。

© www.soinside.com 2019 - 2024. All rights reserved.