簡體   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