【问题标题】:Trouble freeing 2d array in 2d array in c在c中的二维数组中释放二维数组的问题
【发布时间】:2020-01-18 21:44:54
【问题描述】:

我的程序的简要概述:它包含大约 500 组初始参数的列表。然后它将这 500 个分成 50 个“块”,这样它就可以计算 50 个并将结果写入文件,然后转到下一个 50 个“块”,等等。这是一个检查点系统。无论如何,对于每个“块”,都会创建一个名为 calc_result 的结构数组来保存 50 个结果。每个calc_result 都包含一个名称、一个索引和一个用于实际结果的二维字符数组。然后工作完成并写入文件,当前的calc_result 数组被释放。这是基本程序:

typedef struct
{
    char* name;
    int index;
    char** finals;
} calc_results;

int main(int argc, char** argv)
{
    //read in sets of initial parameters
    int numChunks = 10;
    int calcsPerChunk = 50;

    for(int i = 0; i < numChunks; i++)
    {
        calc_result** results = malloc(sizeof(calc_result*)*calcsPerChunk);
        for(int j = 0; j < calcsPerChunk; j++)
        {
             results[j] = malloc(sizeof(calc_result));
             results[j]->name = "blah";
             results[j]->index = j;
             results[j]->finals = malloc(sizeof(char*) * 12); //12 final results
             for(int k = 0; k < 12; k++)
             {
                 results[j]->finals[k] = malloc(70); //max string size is 70
             }
         }

         //DO ACTUAL WORK

         //WRITE RESULTS TO FILE

         //FREE STUFF:
         for(int a = 0; a < calcsPerChunk; a++)
         {
             for(int b = 0; b < 12; b++)
             {
                 free(results[a]->finals[b]);
             }
             free(results[a]->name);
             free(results[a]->finals);
             free(results[a]);
         }

      free(results);
     }
}

我无法释放结果。该程序运行了大约 10 个“块”中的 7 个,并运行了释放 calcsPerChunk 中的 1 个(即 i = 8a = 1b = 0),然后它抛出一个指向 free(results[a]-&gt;finals[b]) 的错误线。错误无用:“Program.exe”已触发断点。”我不确定我在这里做错了什么,有人可以帮忙吗?

【问题讨论】:

  • 你不检查内存分配malloc()是否成功。
  • free(results[a]-&gt;finals[b]); 导致错误。
  • 代码越来越长,所以我去掉了检查和 memset。分配内存时不报错。
  • free(results[a]-&gt;name); 分配在哪里? (这是个大问题)
  • @EsmaeelE - results[j]-&gt;finals[k] = malloc(70) 很好。 sizeof (char) 始终是 1

标签: c multidimensional-array memory-management heap-memory free


【解决方案1】:

注意:您的代码中没有数组,因此您的问题标题用词不当。您正在分配一个 pointer-to-pointer to type,您首先在其中分配指针,然后为每个对象分配一个存储块,然后将每个存储块的起始地址分配给一个指针。 (规则:指针不是数组,数组也不是指针——尽管根据C11 Standard - 6.3.2.1(p3),数组在访问时会转换为指向第一个元素的指针)


您唯一的问题是尝试free (results[a]-&gt;name); 未分配,然后您未能验证每个分配的返回。除此之外,你非常非常接近。你知道free 需要什么以及执行它的顺序,free(results[a]-&gt;name); 看起来更像是 "...duh..." 错误,而不是任何理解失败。

您还可以通过使用取消引用的指针来调整每个分配的类型,而不是尝试为sizeof(X-type)调用X-type,这可能容易出错在情况下。例如,而不是:

    calc_result** results = malloc(sizeof(calc_result*)*calcsPerChunk);

您可以使用sizeof *results(取消引用的指针)来设置每个对象的大小,例如

    calc_results **results = malloc (sizeof *results * calcsPerChunk);

注意:'*''**' 通常与指针而不是类型一起使用。为什么?,语义和使指针是明确的。例如:

    calc_results* a, b, c;

当然不会声明 三指针 calc_result。相反,它声明 指向 calc_result a 和两个 struct calc_result b, c 的指针。确保 '*' 在指针上可以清楚地表明这一点,例如

    calc_results *a, b, c;

(从法律上讲,这没什么区别,编译器可以毫无问题地解析它——问题往往发生在人的一方)

确认您的方法的快速示例

对于测试,您不需要:

int numChunks = 10;
int calcsPerChunk = 50;

35(或 1 以外的任何东西都可以)

整理一个简短的示例,通过使用取消引用的指针验证每个分配以及每个分配的大小,(并为每个级别吐出输出只是为了好玩)你可以这样做:

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

typedef struct {
    char *name;
    int index;
    char **finals;
} calc_results;

int main(void)
{
    int numChunks = 3;
    int calcsPerChunk = 5;

    for(int i = 0; i < numChunks; i++) {
        calc_results **results = malloc (sizeof *results * calcsPerChunk);
        if (!results) {
            perror ("malloc-results");
            return 1;
        }
        for(int j = 0; j < calcsPerChunk; j++)
        {
            if (!(results[j] = malloc (sizeof *results[j]))) {
                perror ("malloc-results[j]");
                return 1;
            }
            results[j]->name = "blah";
            results[j]->index = j;
            if (!(results[j]->finals=malloc(sizeof *results[j]->finals*12))) {
                perror ("malloc-results[j]->finals");
                return 1;
            }
            for(int k = 0; k < 12; k++) {
                if (!(results[j]->finals[k] = malloc(70))) {
                    perror ("malloc-results[j]->finals[k]");
                    return 1;
                }
                sprintf (results[j]->finals[k], "grade %d", k+1);
            }
        }

        /* DO ACTUAL WORK */

        /* output & free stuff */
        printf ("results[%2d]\n", i);
        for (int a = 0; a < calcsPerChunk; a++) {
            printf ("  %s %2d\n", results[a]->name, results[a]->index);
            for (int b = 0; b < 12; b++) {
                printf ("    %s\n", results[a]->finals[b]);
                free (results[a]->finals[b]);
            }
            // free(results[a]->name);
            free(results[a]->finals);
            free(results[a]);
        }

        free(results);
    }
}

内存使用/错误检查

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

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

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

$ valgrind ./bin/free_nested_struct
==13663== Memcheck, a memory error detector
==13663== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==13663== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==13663== Command: ./bin/free_nested_struct
==13663==
results[ 0]
  blah  0
    grade 1
    grade 2
    grade 3
    grade 4
    grade 5
    grade 6
    ...
    <snip>
==13663==
==13663== HEAP SUMMARY:
==13663==     in use at exit: 0 bytes in 0 blocks
==13663==   total heap usage: 213 allocs, 213 frees, 14,520 bytes allocated
==13663==
==13663== All heap blocks were freed -- no leaks are possible
==13663==
==13663== For counts of detected and suppressed errors, rerun with: -v
==13663== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

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

查看一下,如果您还有其他问题,请告诉我。你真的非常接近,我怀疑你实际上是在你的实际代码中分配name,所以你的代码很可能应该工作。

【讨论】:

  • 谢谢你。你是对的 - 我已经在我的代码中 malloc 了这个名字,但忘记把它放在代码示例中。您对内存错误测试的评论让我思考,我确实在此 calc_results 分配代码之前使用的单独函数中发现了内存泄漏。它已修复,现在 calc_results 的东西可以工作了!
  • 不客气。很明显你明白你在做什么。深度嵌套分配的唯一挑战是戴上您的“会计帽”并点上所有"I's" 并交叉所有"T's",以确保每个分配都以正确的顺序与free() 配对。没有谜团,只是很好的旧式复查(并使用内存检查程序确保您是对的)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-15
  • 2011-08-05
  • 1970-01-01
  • 2021-04-08
  • 2016-01-08
  • 2018-09-01
相关资源
最近更新 更多