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

C-将指针重新分配到结构组件(指针类型)已中止(内核转储),c,pointers,memory,struct,allocation,C,Pointers,Memory,Struct,Allocation,我想编程一个图形,但当我想添加顶点时,我会遇到问题。当我想重新分配内存时,程序停止,控制台只是说“中止(内核转储)” 这是我的密码 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> struct Graph { int VertexCounter; struct Vertices *vertex; struct Edge

我想编程一个图形,但当我想添加顶点时,我会遇到问题。当我想重新分配内存时,程序停止,控制台只是说“中止(内核转储)”

这是我的密码

#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;
}
这将返回与以前相同的输出

更新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,但输出不会更改。

正如上面的评论所指出的,您需要跟踪更改的时间,或者可能更改graphPointer。例如,无论何时调用realloc,系统都可能被迫移动您的结构,并返回指向新分配的新指针。同样,正如所指出的,这个新指针被丢弃,例如,在addVertex的末尾。如果要更改函数中的指针,就像任何其他“通过引用调用”参数一样,请向其传递指针。(如果您对类型非常严格,并且只有在确定需要时才进行类型转换,那么编译器会提供帮助。)


您真正需要做的另一件事是检查调用的返回值。有时,您认为一个调用可以正常工作,但它不能正常工作,并且总是返回NULL,因为您的参数是错误的。不管是哪种方式,你都不希望你的程序在没有任何原因说明的情况下在未来某个时候崩溃。例如,如果realloc失败,至少打印realloc失败并退出。

好的,我已经总结了您在示例中试图做的事情。您遇到的一个逻辑问题是
initGraph
。在这里,您要为
结构图
分配,而不是
结构顶点
结构边**adjMat。除非要为其中一个添加值,否则只需将这些指针设置为
NULL
,直到实际需要为其中一个进行分配为止

首先,让我们重新排序您的
结构
定义,以便在需要之前定义作为另一个结构一部分的任何结构,例如

#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;
};
上面,
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
中不会更改。您所做的只是在
graphpointer
中分配
struct Graph
的一个成员,该成员可以简单地分配给
顶点
指针。现在,当您准备添加顶点时,可以执行以下操作:

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
中,所有操作都是
graphpointer
struct vertexs
成员,这也是应该的。如果在这一点上添加一个顶点,请考虑将其作为参数(或参数中的数据)传递,以便顶点数据可以在<代码> AdvEdTrx<代码>中被分配/复制,以将所有添加顶点代码放在一个地方。 在您编写的任何动态分配内存的代码中,对于所分配的任何内存块,您有两个责任:(1)始终保留指向内存块起始地址的指针,以便(2)在不再需要它时可以释放它。到目前为止分配给
freeGraph
的快速
freeGraph
功能可能是:

void freeGraph (struct Graph *graph)
{
    free (graph->vertex);
    free (graph);
}
(在为
adjMat
添加分配时,不要忘记在
freeGraph
中添加相应的
free

总而言之,你可以做:

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

*/
内存使用/错误检查

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

对于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等的初始块。
然后当
VertexCounter==VertexAllocated
时,您可以调用
realloc
然后和
realloc
两次
VertexAllocated
,然后继续


仔细检查一下,如果有问题请告诉我。

void addVertex(struct Graph**Graph)
google如何在C中更新函数内的指针。我将尝试一下,并对我的结果发表评论。感谢@rafix07valgrind在addVertex()中的realloc()调用中显示了一个无效的free。我发布了关于您建议的更新,但这也不起作用@RAFIX07使用
void initGraph(struct Graph*Graph)
等所做的一切。。我
#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);

*/
$ ./bin/graphalloc
Number of Vertices: 0
Number of Vertices: 1
$ 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)