繁体   English   中英

无法在C中释放2D数组中的2D数组

[英]Trouble freeing 2d array in 2d array in c

我的程序的简要概述:它包含约500组初始参数的列表。 然后将这500个拆分成50个“块”,以便可以计算出50个并将结果写入文件,然后移至下一个“块” 50个,依此类推。这是一个检查点系统。 无论如何,对于每个“块”, calc_result创建一个称为calc_result的结构数组,以容纳50个结果。 每个calc_result由一个名称,一个索引和一个用于实际结果的2d char数组组成。 然后完成工作并将其写入文件,并释放当前的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]->finals[b])的错误free(results[a]->finals[b])行。 该错误是没有用的:“ Program.exe”已触发断点。“我不确定我在做什么错,有人可以帮忙吗?

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


您唯一的问题是尝试free (results[a]->name); 没有分配,然后您无法验证每个分配的返回 除此之外,您非常亲密。 您知道需要free以及free的顺序,即free(results[a]->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声明3指针 相反,它声明了指向 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 ,因此很有可能您的代码应该可以工作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM