如何修复C中的'从0x4000000000000000读取内存失败(读取4个字节中的0个)'错误

时间:2019-04-04 23:43:20

标签: c malloc

我在C上做一个简单的练习。但是,当我使用malloc初始化变量图时,它无法正确执行操作。这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef int Weight;

typedef struct aux {
    int vDest;
    Weight weight;
    struct aux * next;
} TypeEdge;

typedef TypeEdge* TypePointer;

typedef struct {
    TypePointer * listAdj;
    int numVertices;
    int numEdges;
} TypeGraph;

typedef int* TipoInt;

bool initializeGraph(TypeGraph *graph, int nv) {
    if (nv < 0) {
        return false;
    }

    graph->numVertices = nv;
    graph->numEdges = 0;
    int i;
    for (i = 0; i < nv; i++) {
        graph->listAdj = (TypePointer*) malloc(sizeof(TypePointer));
    }
    for (i = 0; i < nv; i++) {
        graph->listAdj[i] = NULL;
    }
    return true;
}

void insertEdge(int v1, int v2, Weight weight, TypeGraph *graph) {
    if (v1 < 0 || v1 > graph->numVertices || v2 < 0 || v2 > graph->numVertices) {
        return;
    }
    TypePointer actual = graph->listAdj[v1];
    while (actual->next) {
        actual = actual->next;
    }
    TypePointer pNew = (TypePointer) malloc(sizeof(TypeEdge));
    pNew->vDest = v2;
    pNew->weight = weight;
    pNew->next = NULL;
    actual->next = pNew;
}

int main() {
    TypeGraph graph;
    bool result = initializeGraph(&graph, 100);
    if (result) {
        insertEdge(2, 3, 1, &graph);
    }
    return 0;
}

问题在于,它没有初始化大小为100 TypePointer的图形,而是仅初始化大小为2且不执行任何操作。当我尝试在Clion上对其进行调试时,它显示以下错误消息:read memory from 0x4000000000000000 failed (0 of 4 bytes read)。如果我只运行代码,它将返回代码11。

请,有人可以帮我吗?

2 个答案:

答案 0 :(得分:0)

您的手上确实乱七八糟。由指针的typedef贡献,但是主要是因为您未能分配nv指针,而是分配了相同的listAdj指针,然后立即用{{1}覆盖指针}造成100次内存泄漏。

考虑一下:

NULL

(无需强制返回 for (i = 0; i < nv; i++) { graph->listAdj = (TypePointer*) malloc(sizeof(TypePointer)); } 的返回值,这是不必要的。请参阅:Do I cast the result of malloc?和...始终验证每个分配)

malloc是一个单指针,它在每次迭代时将地址保存到新分配的内存块中,然后再由随后的每次分配覆盖。

此外,您然后尝试取消引用覆盖的指针,并将不存在的指针设置为graph->listAdj,例如:

NULL

您仅尝试分配一个 for (i = 0; i < nv; i++) { graph->listAdj[i] = NULL; } 指针(尽管100次),而不是按预期分配graph->listAdj指针。您必须立即为nv指针分配存储空间。

现在让我们从头开始,并删除所有nv,然后将typedef用于int,例如

bool

现在,所有查看您的代码的人都清楚地知道#include <stdio.h> #include <stdlib.h> typedef struct aux { int vDest; int weight; struct aux *next; } TypeEdge; typedef struct { TypeEdge **listAdj; int numVertices; int numEdges; } TypeGraph; listAdj pointer-to-pointer 。无需猜测,也不必在以后四处寻找,而想知道以后TypeEdge是300行,TypePointerTypeEdge,仅此而已。

TypeEdge时,您需要分配所有initializeGraph指针是对nv的单个调用,而不是循环。然后,您可以遍历所有设置为malloc的指针,例如

NULL

下一步,当您int initializeGraph (TypeGraph *graph, int nv) { if (nv < 0) return 0; graph->numVertices = nv; graph->numEdges = 0; int i; if ((graph->listAdj = malloc(sizeof *graph->listAdj * nv)) == NULL) { perror ("malloc-graph->listAdj"); return 0; } for (i = 0; i < nv; i++) (graph->listAdj)[i] = NULL; return 1; } 时,必须处理要为该顶点插入第一条边的情况,或者是否需要迭代到列表的末尾并在此插入。您还需要调整迭代的方式,以确保如果insertEdge()actual->next,则不会尝试访问actual。将它们放在一起,您可以执行以下操作:

NULL

注意:该函数的返回类型是如何从TypeEdge *insertEdge (int v1, int v2, int weight, TypeGraph *graph) { if (v1 < 0 || v1 > graph->numVertices || v2 < 0 || v2 > graph->numVertices) { return NULL; } TypeEdge *actual = graph->listAdj[v1]; while (actual && actual->next) actual = actual->next; TypeEdge *pNew = malloc(sizeof *pNew); if (!pNew) { perror ("malloc-pNew"); return NULL; } pNew->vDest = v2; pNew->weight = weight; pNew->next = NULL; if (!actual) graph->listAdj[v1] = pNew; else actual->next = pNew; return (pNew); } 更改为TypeEdge *的,因此您可以获得有意义的返回值,可以表明您尝试的成功/失败插入边缘。切勿在{{1​​}}函数内进行分配,而无需向调用函数指出分配(以及其余关键步骤)是成功还是失败)

您的void尝试进行void时,它不会给您任何指示。除非您有某种方法可以验证您实际插入了边缘,否则您会感到疑惑。只需编写简短的main()函数集即可处理输出包含每个顶点的边的列表,例如

insertEdge()

如果已分配内存,则还需要释放该内存。类似的简短函数集可以处理释放每个列表的问题,例如

print

您可以通过以下方式致电void prnedge (const TypeEdge *e) { do printf (" %3d %3d\n", e->vDest, e->weight); while ((e = e->next)); } void print_edge (const TypeEdge *e, int edge) { printf ("\nedge %d\n", edge); prnedge (e); } void print_graph (const TypeGraph *g) { for (int i = 0; i < g->numVertices; i++) if (g->listAdj[i]) print_edge (g->listAdj[i], i); }

void freelist (TypeEdge *l)
{
    while (l) {
        TypeEdge *victim = l;
        l = l->next;
        free (victim);
    }
}

void free_graphlists (TypeGraph *g)
{
    for (int i = 0; i < g->numVertices; i++)
        if (g->listAdj[i])
            freelist (g->listAdj[i]);

    free (g->listAdj);
}

完全将其放入,您可以这样做:

main()

使用/输出示例

int main (void) {

    TypeGraph graph;
    int result = initializeGraph (&graph, 100);

    if (result) {
        insertEdge (2, 3, 1, &graph);
        insertEdge (2, 4, 1, &graph);
    }

    print_graph (&graph);
    free_graphlists (&graph);

    return 0;
}

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个职责:(1)始终保留指向起始地址的指针因此,(2)当不再需要它时可以释放

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

对于Linux,#include <stdio.h> #include <stdlib.h> typedef struct aux { int vDest; int weight; struct aux *next; } TypeEdge; typedef struct { TypeEdge **listAdj; int numVertices; int numEdges; } TypeGraph; int initializeGraph (TypeGraph *graph, int nv) { if (nv < 0) return 0; graph->numVertices = nv; graph->numEdges = 0; int i; if ((graph->listAdj = malloc(sizeof *graph->listAdj * nv)) == NULL) { perror ("malloc-graph->listAdj"); return 0; } for (i = 0; i < nv; i++) (graph->listAdj)[i] = NULL; return 1; } TypeEdge *insertEdge (int v1, int v2, int weight, TypeGraph *graph) { if (v1 < 0 || v1 > graph->numVertices || v2 < 0 || v2 > graph->numVertices) { return NULL; } TypeEdge *actual = graph->listAdj[v1]; while (actual && actual->next) actual = actual->next; TypeEdge *pNew = malloc(sizeof *pNew); if (!pNew) { perror ("malloc-pNew"); return NULL; } pNew->vDest = v2; pNew->weight = weight; pNew->next = NULL; if (!actual) graph->listAdj[v1] = pNew; else actual->next = pNew; return (pNew); } void prnedge (const TypeEdge *e) { do printf (" %3d %3d\n", e->vDest, e->weight); while ((e = e->next)); } void print_edge (const TypeEdge *e, int edge) { printf ("\nedge %d\n", edge); prnedge (e); } void print_graph (const TypeGraph *g) { for (int i = 0; i < g->numVertices; i++) if (g->listAdj[i]) print_edge (g->listAdj[i], i); } void freelist (TypeEdge *l) { while (l) { TypeEdge *victim = l; l = l->next; free (victim); } } void free_graphlists (TypeGraph *g) { for (int i = 0; i < g->numVertices; i++) if (g->listAdj[i]) freelist (g->listAdj[i]); free (g->listAdj); } int main (void) { TypeGraph graph; int result = initializeGraph (&graph, 100); if (result) { insertEdge (2, 3, 1, &graph); insertEdge (2, 4, 1, &graph); } print_graph (&graph); free_graphlists (&graph); return 0; } 是正常选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。

$ ./bin/edgetype

edge 2
   3   1
   4   1

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

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

答案 1 :(得分:0)

您的initializeGraphinsertEdge中出了点问题。此更改应该对您有用

bool initializeGraph(TypeGraph *graph, int nv)
{
    if (nv < 0) {
        return false;
    }

    graph->numVertices = nv;
    graph->numEdges = 0;

    /* call `calloc` to avoid using for-loop to zero memory */
    graph->listAdj = calloc(nv, sizeof(TypePointer));

    return true;
}

void insertEdge(int v1, int v2, Weight weight, TypeGraph *graph)
{
    if (v1 < 0 || v1 > graph->numVertices || v2 < 0 || v2 > graph->numVertices) {
        return;
    }

    /* just insert the node at the head of the adjList */
    TypePointer pNew = malloc(sizeof(TypeEdge));

    pNew->vDest = v2;
    pNew->weight = weight;
    pNew->next = graph->listAdj[v1];
    graph->listAdj[v1] = pNew;
    graph->numEdges++;
}

对于 typedef ,请阅读Why should we typedef a struct so often in C?The Linux Kernel CodingStyle document,以自己决定是否使用它。就我个人而言,除非必要,否则我本人避免使用typedef