【问题标题】:2D Array Memory Allocation (malloc) Returns NULL2D 数组内存分配 (malloc) 返回 NULL
【发布时间】:2017-07-08 08:35:00
【问题描述】:

我正在尝试使用 GCC 编译 64 位 CPP 代码,但是一旦我增加元素大小,多维(即 2D)数组内存分配将返回 NULL46,00046,500。我的虚拟内存设置为 96GB,硬件使用 32GB 内存运行 64 位操作系统。 只要 MAX_VERTICES 不超过 46000,代码就可以正常工作。

以下是我试图动态分配的内容:

struct ShortestPath {
    real32 totalWeight;
    // NOTE: ShortestPath is a list of pointers; does not make copies 
    // (i.e. side-effects) the pointers point to memory allocated
    // in the DijkstraSPTree array in the vehicle_searching module
    List<DirectedEdge *> *edgeList;
};

#define MAX_VERTICES 46500
global_variable ShortestPath spAllPairs[MAX_VERTICES][MAX_VERTICES];

在堆上分配内存来替换

spAllPairs[MAX_VERTICES][MAX_VERTICES]

使用以下代码

global_variable ShortestPath **spAllPairs;
global_variable ShortestPath *arr_data;

ShortestPath *getShortestPath(EdgeWeightedDigraph *digraph, int32 source,
                              int32 dest)
{
    free(spAllPairs); // Function is called multiple times so I clear memory
    free(arr_data); // before reallocation given values pointed by pointers
    free(spTreesArray); // are used in other files in my project after run.

    inline allocate_mem(ShortestPath*** arr, ShortestPath** arr_data, int n, int m);
    allocate_mem(&spAllPairs, &arr_data, MAX_VERTICES, MAX_VERTICES);
    for (unsigned int k = 0 ; k < MAX_VERTICES ; k++) {
        if (spAllPairs[k] == NULL) {
            while (k >= 1) {
                free(spAllPairs[k]);
                --k;
            }
            free(spAllPairs[0]);
            free(spAllPairs);
            fprintf(stderr, "Failed to allocate space for Shortest Path Pairs!\n");
            exit(1);
        }
    }

    spTreesArray = (DijkstraSPTree *)malloc(MAX_VERTICES * sizeof(DijkstraSPTree));
    for (int32 vertexTo = 0; vertexTo < digraph->vertices; ++vertexTo) {
        pathTo(&spTreesArray[source], &spAllPairs[source][vertexTo],
                   vertexTo);
    }
    return &spAllPairs[source][dest];
}

void pathTo(DijkstraSPTree *spTree, ShortestPath *shortestPath, int32 dest)
{
    List<DirectedEdge *>::traverseList(freeDirectedEdge, shortestPath->edgeList);
    List<DirectedEdge *>::emptyList(&shortestPath->edgeList);
    shortestPath->totalWeight = spTree->distTo[dest];
}

int allocate_mem(ShortestPath ***arr, ShortestPath **arr_data, int n, int m)
{
    *arr = (ShortestPath **)malloc(n * sizeof(ShortestPath*));
    *arr_data = (ShortestPath *)malloc(n * m * sizeof(ShortestPath));
    for (int i = 0; i < n; i++)
        (*arr)[i] = *arr_data + i * m;
    return 0; //free point
}

【问题讨论】:

  • 记住堆上的分配必须是连续的。您尝试将 32 gigs 的内存(如果sizeof(ShortestPath) == 16 最有可能是)分配为一大块。如果没有这么大的连续内存块,分配将失败。
  • 46000 x 46000 低于 1.97 Gigs,而 46500 x 46500 低于 2.013 Gigs。如果你的结构的大小是 16,那么46000 可能会保持在 32 GB 以下,而 46500 会产生超过 32 GB。检查你的mallocs 的结果,他们应该在某个时候得到 NULL
  • @StephanLechner 物理内存无关紧要,因为我使用的是 malloc,而虚拟内存为 96GB。我不确定它是否是连续的。在“spAllPairs”初始化期间,我在 46500x46500 处得到 NULL。
  • @Someprogrammerdude 是的,ShortestPath 的大小 = 16,如您所指。在这种情况下我该怎么办?重新启动我的电脑或增加虚拟内存的大小有帮助吗?谢谢
  • "我的虚拟内存设置为 96GB,硬件运行 64 位操作系统,使用 32GB 内存" 你喜欢颠簸吗?这就是你如何得到鞭打。

标签: c++ arrays memory-management malloc


【解决方案1】:

函数allocate_memgetShortestPath 中用于释放结构的代码不一致。如果arr_data 没有在其他地方使用,你应该删除这个全局变量并以这种方式分配一个间接数组:

ShortestPath **allocate_mem(int n, int m) {
    ShortestPath **arr = (ShortestPath **)calloc(n, sizeof(*arr));
    if (arr != NULL) {
        for (int i = 0; i < n; i++) {
            arr[i] = (ShortestPath *)calloc(m, sizeof(ShortestPath));
            if (arr[i] == NULL)
                break;
        }
    }
    return arr;
}

注意事项:

  • free 指向的内存之后将NULL 存储到全局指针中会更安全。
  • allocate_mem 检查它是否可以分配所有数组元素并释放分配的所有元素,而不是尝试在调用函数中清理,这将更加一致。

这里是比较一致的版本和调用代码:

    ShortestPath **allocate_mem(int n, int m) {
        ShortestPath **arr = (ShortestPath **)calloc(n, sizeof(*arr));
        if (arr != NULL) {
            for (int i = 0; i < n; i++) {
                arr[i] = (ShortestPath *)calloc(m, sizeof(ShortestPath));
                if (arr[i] == NULL) {
                    for (j = i; j-- > 0;) {
                        free(arr[j]);
                    }
                    free(arr);
                    return NULL;
                }
            }
        }
        return arr;
    }

    ShortestPath *getShortestPath(EdgeWeightedDigraph *digraph, int32 source,
                                  int32 dest)
    {
        // Function is called multiple times so I clear memory
        // before reallocation given values pointed by pointers
        // are used in other files in my project after run.
        free(spAllPairs);
        spAllPairs = NULL;
        free(arr_data);
        arr_data = NULL;
        free(spTreesArray);
        spTreesArray = NULL;

        spAllPairs = allocate_mem(MAX_VERTICES, MAX_VERTICES);
        if (spAllPairs == NULL) {
            fprintf(stderr, "Failed to allocate space for Shortest Path Pairs!\n");
            exit(1);
        }

        spTreesArray = (DijkstraSPTree *)malloc(MAX_VERTICES * sizeof(DijkstraSPTree));
        if (spTreesArray == NULL) {
            fprintf(stderr, "Failed to allocate space for DijkstraSPTree!\n");
            exit(1);
        }
        for (int32 vertexTo = 0; vertexTo < digraph->vertices; ++vertexTo) {
            pathTo(&spTreesArray[source], &spAllPairs[source][vertexTo],
                   vertexTo);
        }
        return &spAllPairs[source][dest];
    }

EDIT 正如 M.M 评论的那样,您应该在 C++ 中使用 newdelete 运算符,而不是 malloc()free()。 (或者除了malloc,但为什么还要打扰malloc):

ShortestPath **allocate_mem(int n, int m) {
    ShortestPath **arr = new ShortestPath *[n];
    if (arr != NULL) {
        for (int i = 0; i < n; i++) {
            arr[i] = new ShortestPath[m];
            if (arr[i] == NULL) {
                for (j = i; j-- > 0;) {
                    delete[] arr[j];
                }
                delete[] arr;
                return NULL;
            }
        }
    }
    return arr;
}

ShortestPath *getShortestPath(EdgeWeightedDigraph *digraph, int32 source,
                              int32 dest)
{
    // Function is called multiple times so I clear memory
    // before reallocation given values pointed by pointers
    // are used in other files in my project after run.
    delete[] spAllPairs;
    spAllPairs = NULL;
    delete[] spTreesArray;
    spTreesArray = NULL;

    spAllPairs = allocate_mem(MAX_VERTICES, MAX_VERTICES);
    if (spAllPairs == NULL) {
        fprintf(stderr, "Failed to allocate space for Shortest Path Pairs!\n");
        exit(1);
    }

    spTreesArray = new DijkstraSPTree *[MAX_VERTICES];
    if (spTreesArray == NULL) {
        fprintf(stderr, "Failed to allocate space for DijkstraSPTree!\n");
        exit(1);
    }
    for (int32 vertexTo = 0; vertexTo < digraph->vertices; ++vertexTo) {
        pathTo(&spTreesArray[source], &spAllPairs[source][vertexTo],
               vertexTo);
    }
    return &spAllPairs[source][dest];
}

【讨论】:

  • 使用 malloc-family 然后写入空间是未定义的行为,而不是通过placement-new创建对象。 (所以你应该首先使用new)。
  • 错误:(*arr)[i] = new ShortestPath[m]; 不匹配 operator= 操作数类型为 ShortestPathShortestPath*
  • 刚刚用 2d 向量替换了 2d 数组,它就像一个魅力。无需使用 malloc 并使代码进一步复杂化。
  • @Far:我的错,树星程序员在天黑后抓住了我。答案已更新。
猜你喜欢
  • 2021-05-29
  • 1970-01-01
  • 1970-01-01
  • 2012-12-26
  • 2012-09-02
  • 2023-04-01
  • 2015-06-14
  • 2016-05-24
  • 2021-12-12
相关资源
最近更新 更多