C - 内存泄漏问题排查

时间:2016-11-18 23:35:32

标签: c matrix memory-management memory-leaks valgrind

所以我制作了一个读取矩阵的代码,然后用它们进行计数。

我分配内存来存储矩阵并用更多分配的数组填充它并最终释放所有内容,但是Valgrind告诉我,当我分配内存然后不要&#时内存泄漏39;释放它当我释放它时,程序不起作用,我得到了SIGSEGV。它如下:

int main() {
    int ***arrMatrices= (int***) calloc(100, sizeof(int**));
    char *operations = (char*) calloc(100, sizeof(char));

    int heights[100], widths[100];
    int noOfMatrices= 0, output = 0;

    ...

    output = readMatrix(arrMatrices, &noOfMatrices, heights, widths);

    ...

    freeEverything(arrMatrices,noOfMatrices,heights,widths,operations);
    return (0);
}

int readMatrix(int ***arrMatrices, int *noOfMatrices, int *heights, int *widths){
    int height, width;
    int output = scanf("%d %d",&height, &width);
    int result = 1;

    ...

    int **matrix = (int**) calloc(height, sizeof(int*));

    for(int i = 0; i < height; i++){
        int *row = (int*) calloc(width, sizeof(int));
        for(int y = 0; y < width; y++){
            output = scanf("%d",(row+y));
            if(output < 1) result = -1;
        }
        matrix[i] = row;
    }

    arrMatrices[*noOfMatrices] = matrix;

    heights[*noOfMatrices] = height;
    widths[*noOfMatrices] = width;
    *noOfMatrices+=1;

    return result;
}

void freeEverything(int ***arrMatrices, int noOfMatrices, int *heights, int *widths, char *operations){
    for(int i = 0; i < noOfMatrices; i++){
        for(int row = 0; row < heights[i]; row++){
            free(arrMatrices[i][row]);
        }
        printf("%d\n",i);
        free(arrMatrices[i]);
    }
    free(arrMatrices);
    free(operations);
}

所以问题是我读取矩阵,将其加载到我的数组中并返回。如果我试图释放它,我会得到一个错误,并且显然最终将它全部释放 - 逐行和矩阵跟矩阵跟随我的整个数组 - 不够。 Valgrind明确表示它是readMatrix中的矩阵分配。

谁能告诉我如何正确地做到这一点?

编辑(根据建议添加代码+编辑):

这是我的代码的另一个片段 - 以下函数将矩阵相乘。在那里我以相同的方式声明矩阵,但我没有内存泄漏。

int multiply(int ***arrOfMatrices, int firstIndex, int secondIndex, int *heights, int *widths){
    if(widths[firstIndex] != heights[secondIndex]) return -1;

    int **matrix = (int**) calloc(heights[firstIndex],sizeof(int*));

    for(int i = 0; i < heights[firstIndex]; i++){
        int *row = (int*) calloc(widths[secondIndex], sizeof(int));
        for(int y = 0; y < widths[secondIndex]; y++){
            int sum = 0;
            for(int j = 0; j < widths[firstIndex]; j++){
                sum = sum + (arrOfMatrices[firstIndex][i][j] * arrOfMatrices[secondIndex][j][y]);
            }
            row[y] = sum;
        }
        matrix[i] = row;
    }

    arrOfMatrices[secondIndex] = matrix;
    heights[secondIndex] = heights[firstIndex];

    return 1;
}

编辑2 (发布整个代码):

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

void printMatrix(int ***arrOfMatrices, int index, int *heights, int *widths){
    int height = heights[index];
    int width = widths[index];

    printf("%d %d\n",height, width);
    for(int i = 0; i < height; i++){
        printf("%d",arrOfMatrices[index][i][0]);
        for(int y = 1; y < width; y++){
            printf(" %d",arrOfMatrices[index][i][y]);
        }
        printf("\n");
    }
}

int readMatrix(int ***arrOfMatrices, int *noOfMatrices, int *heights, int *widths){
    int height, width;
    int output = scanf("%d %d",&height, &width);
    int result = 1;

    if(output < 2){
        fprintf(stderr,"Error\n"); return 100;
    }

    int **matrix = (int**) calloc(height, sizeof(int*));

    for(int i = 0; i < height; i++){
        int *row = (int*) calloc(width, sizeof(int));
        for(int y = 0; y < width; y++){
            output = scanf("%d",(row+y));
            if(output < 1) result = -1;
        }
        matrix[i] = row;
    }

    if(result == -1){
        for(int i = 0; i < height; i++){
            free(matrix[i]);
        }
        free(matrix);
        return result;
    }

    arrOfMatrices[*noOfMatrices] = matrix;

    heights[*noOfMatrices] = height;
    widths[*noOfMatrices] = width;
    *noOfMatrices+=1;

    return result;
}

int multiply(int ***arrOfMatrices, int firstIndex, int secondIndex, int *heights, int *widths){
    if(widths[firstIndex] != heights[secondIndex]) return -1;

    int **matrix = (int**) calloc(heights[firstIndex],sizeof(int*));

    for(int i = 0; i < heights[firstIndex]; i++){
        int *row = (int*) calloc(widths[secondIndex], sizeof(int));
        for(int y = 0; y < widths[secondIndex]; y++){
            int sum = 0;
            for(int j = 0; j < widths[firstIndex]; j++){
                sum = sum + (arrOfMatrices[firstIndex][i][j] * arrOfMatrices[secondIndex][j][y]);
            }
            row[y] = sum;
        }
        matrix[i] = row;
    }

    arrOfMatrices[secondIndex] = matrix;
    heights[secondIndex] = heights[firstIndex];

    //free(matrix);
    return 1;
}

int addSubstract(int ***arrOfMatrices, int firstIndex, int secondIndex, int *heights, int *widths, int modificator){
    if(heights[firstIndex] != heights[secondIndex] || widths[firstIndex] != widths[secondIndex]) return -1;

    for(int i = 0; i < heights[firstIndex]; i++){
        for(int y = 0; y < widths[secondIndex]; y++){
            arrOfMatrices[secondIndex][i][y] = (modificator * arrOfMatrices[secondIndex][i][y]) + arrOfMatrices[firstIndex][i][y];
        }
    }    

    return 1;
}

int countPriorityOperations(int ***arrOfMatrices, char *operations, int noOfMatrices, int *heights, int *widths){
    /*
        Picks all multiplications and counts 'em first
    */
    int output;
    for(int i = 0; i < noOfMatrices-1;i++){
        if(operations[i] == '*'){
            output = multiply(arrOfMatrices,i,i+1,heights,widths);
            if(output == -1) return -1;
        }
    }
    return 1;
}

int countRemainingOperations(int ***arrOfMatrices, char *operations, int noOfMatrices, int *heights, int *widths){
    /*
        Does all the other operations that aren't of the multiply masterrace
        Skips matrices that have already been multiplied
    */
    for(int i = 0; i < noOfMatrices-1;i++){
        if(operations[i] == '*') continue;
        if(operations[i] == '+' || operations[i] == '-'){
            int modificator = 0;
            if(operations[i] == '+') modificator = 1; else modificator = -1;
            if(operations[i+1] != '*'){
                if(addSubstract(arrOfMatrices,i, i+1, heights, widths, modificator) == -1) return -1;
            }else{
                if(addSubstract(arrOfMatrices,i, i+2, heights, widths, modificator) == -1) return -1;
                ++i;
            }
        }
    }
    return 1;
}

void freeEverything(int ***arrOfMatrices, int noOfMatrices, int *heights, int *widths, char *operations){
    for(int i = 0; i < noOfMatrices; i++){
        for(int row = 0; row < heights[i]; row++){
            free(arrOfMatrices[i][row]);
        }
        free(arrOfMatrices[i]);
    }
    free(arrOfMatrices);
    free(operations);
}

int main() {
    int ***arrOfMatrices = (int***) calloc(100, sizeof(int**));
    char *operations = (char*) calloc(100, sizeof(char));
    int heights[100], widths[100];
    int noOfMatrices = 0, output = 0;

    while(output != EOF){
        output = readMatrix(arrOfMatrices, &noOfMatrices, heights, widths);
        if(output == -1){
            fprintf(stderr,"Error\n"); return 100;
        }
        char temp;
        output = scanf(" %c",&temp);
        if(temp != '+' && temp != '-' && temp != '*' && output != EOF){
            fprintf(stderr,"Error\n"); return 100;
        }
        if(output == EOF) break;
        operations[noOfMatrices-1] = temp;      
    }

    if(countPriorityOperations(arrOfMatrices,operations,noOfMatrices, heights, widths) == -1){
        fprintf(stderr,"Error\n"); return 100;
    }

    if(countRemainingOperations(arrOfMatrices,operations,noOfMatrices, heights, widths) == -1){
        fprintf(stderr,"Error\n"); return 100;        
    }

    printMatrix(arrOfMatrices,noOfMatrices-1,heights,widths);
    freeEverything(arrOfMatrices,noOfMatrices,heights,widths,operations);
    return (0);
}

当所有输入都正确且程序正确完成时,Valgrind输出:

==24== HEAP SUMMARY:
==24==     in use at exit: 72 bytes in 4 blocks
==24==   total heap usage: 21 allocs, 17 frees, 1,244 bytes allocated
==24==
==24== 72 (24 direct, 48 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 2
==24==    at 0x4C2C9B4: calloc (vg_replace_malloc.c:711)
==24==    by 0x400896: readMatrix (in [path])
==24==    by 0x40109C: main (in [path])
==24==
==24== LEAK SUMMARY:
==24==    definitely lost: 24 bytes in 1 blocks
==24==    indirectly lost: 48 bytes in 3 blocks
==24==      possibly lost: 0 bytes in 0 blocks
==24==    still reachable: 0 bytes in 0 blocks
==24==         suppressed: 0 bytes in 0 blocks
==24==
==24== For counts of detected and suppressed errors, rerun with: -v
==24== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

2 个答案:

答案 0 :(得分:1)

编辑问题中的代码以匹配答案中的建议通常是个坏主意。最好添加更新,以便原始代码仍然可以轻松检查。在这种情况下,只要输入了矩阵的所有元素,原始代码就可以正常工作。

您描述的问题似乎仅在为矩阵元素输入非数字值时才会出现。这让我觉得你的意图是提供一种在出错的情况下从矩阵中逃脱的方法,并再次输入该矩阵的值。

正如@Mox指出的那样,你无法在这里释放一些内存。我并不完全清楚你的段错是如何产生的,而且我无法复制这种行为。

我在readMatrix()做了一些更改。遇到非数字输入时(例如'q'),与当前matrix关联的所有分配必须为free d。当然,当前rowmatrix必须为free d,但您还必须free row已存储在matrixfree 1}}。循环实现了这一点,这必须在matrix -1之前完成。无需清除输入流,因为在非数字输入的情况下程序退出并出现错误。最后,return int readMatrix(int ***arrOfMatrices, int *noOfMatrices, int *heights, int *widths){ int height, width; int output = scanf("%d %d",&height, &width); if(output < 2){ fprintf(stderr,"Error\n"); return 100; } int **matrix = (int**) calloc(height, sizeof(int*)); for(int i = 0; i < height; i++){ int *row = (int*) calloc(width, sizeof(int)); for(int y = 0; y < width; y++){ output = scanf("%d",(row+y)); if(output < 1) { for (int j = 0; j < i; j++) // free previous rows free(matrix[j]); free(row); free(matrix); return -1; } } matrix[i] = row; } arrOfMatrices[*noOfMatrices] = matrix; heights[*noOfMatrices] = height; widths[*noOfMatrices] = width; *noOfMatrices+=1; return 1; } 为调用函数。

malloc

此处还有另一个内存泄漏源。每个错误退出点必须在return之前释放所有while(output != EOF){ output = readMatrix(arrOfMatrices, &noOfMatrices, heights, widths); if(output == -1){ freeEverything(arrOfMatrices,noOfMatrices,heights,widths,operations); fprintf(stderr,"Error\n"); return 100; } char temp; output = scanf(" %c",&temp); if(temp != '+' && temp != '-' && temp != '*' && output != EOF){ freeEverything(arrOfMatrices,noOfMatrices,heights,widths,operations); fprintf(stderr,"Error\n"); return 100; } if(output == EOF) break; operations[noOfMatrices-1] = temp; } if(countPriorityOperations(arrOfMatrices,operations,noOfMatrices, heights, widths) == -1){ freeEverything(arrOfMatrices,noOfMatrices,heights,widths,operations); fprintf(stderr,"Error\n"); return 100; } if(countRemainingOperations(arrOfMatrices,operations,noOfMatrices, heights, widths) == -1){ freeEverything(arrOfMatrices,noOfMatrices,heights,widths,operations); fprintf(stderr,"Error\n"); return 100; } 内存:

3 3
1 1 1
1 1 1
1 1 1
+
3 3
1 1 1
1 1 1
1 1 1
3 3
2 2 2
2 2 2
2 2 2
==5049== 
==5049== HEAP SUMMARY:
==5049==     in use at exit: 0 bytes in 0 blocks
==5049==   total heap usage: 10 allocs, 10 frees, 1,020 bytes allocated
==5049== 
==5049== All heap blocks were freed -- no leaks are possible
==5049== 
==5049== For counts of detected and suppressed errors, rerun with: -v
==5049== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

做出这些改变后,我发现没有进一步的泄漏,Valgrind同意:

输入正确:

3 3
1 1 1
1 1 1
1 1 1
+
3 3
1 1 1
1 q
Error
==5050== 
==5050== HEAP SUMMARY:
==5050==     in use at exit: 0 bytes in 0 blocks
==5050==   total heap usage: 9 allocs, 9 frees, 1,008 bytes allocated
==5050== 
==5050== All heap blocks were freed -- no leaks are possible
==5050== 
==5050== For counts of detected and suppressed errors, rerun with: -v
==5050== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

使用非数字输入:

struct

另一方面,三重间接几乎不是正确的答案。在这种情况下,我会考虑使用struct Matrix { int **arr; size_t rows; size_t cols; }; // ... struct Matrix *matrices; 来存储矩阵信息:

heights[]

这样做的好处是可以使用矩阵元素存储行数和列数,这样您就不再需要widths[]int readMatrix(struct Matrix *matrices, int *noOfMatrices); void freeEverything(struct Matrix *matrices, int noOfMatrices, char *operations); 数组。您的函数原型也将被简化:

readMatrix()

修改

当您提供完整的代码时,您应该提供原始代码,而不是根据我们所做的错误假设进行更改的代码。但那没关系;在修复之前,我将multiply()函数更改回原始格式(我认为!)。我认为您遇到的一个问题是,您将@Mox提供的解决方案的元素与我原始解决方案的元素结合在一起。这些解决方案都没有与完整的图片一起工作,这种组合只会让事情变得混乱。

您的matrix功能似乎也存在问题。在这里,您为新arrOfMatrices[]分配了一个乘法结果,然后用这个新矩阵替换free中的矩阵。但是在更换它之前你必须multiply()旧的,否则你会泄漏那个内存,因为你正在丢失对它的引用。以下是更改int multiply(int ***arrOfMatrices, int firstIndex, int secondIndex, int *heights, int *widths){ if(widths[firstIndex] != heights[secondIndex]) return -1; int **matrix = (int**) calloc(heights[firstIndex],sizeof(int*)); for(int i = 0; i < heights[firstIndex]; i++){ int *row = (int*) calloc(widths[secondIndex], sizeof(int)); for(int y = 0; y < widths[secondIndex]; y++){ int sum = 0; for(int j = 0; j < widths[firstIndex]; j++){ sum = sum + (arrOfMatrices[firstIndex][i][j] * arrOfMatrices[secondIndex][j][y]); } row[y] = sum; } matrix[i] = row; } /* Free old allocated memory */ for (int j = 0; j < heights[secondIndex]; j++) free(arrOfMatrices[secondIndex][j]); free(arrOfMatrices[secondIndex]); arrOfMatrices[secondIndex] = matrix; heights[secondIndex] = heights[firstIndex]; //free(matrix); return 1; } 以停止泄漏内存的方法:

<div class="f-wave-send">
    <div class="t__trigger-actions"> <!-- this is the modal trigger button !-->
        <span id="trigger-actions-modal"></span>
    </div>
    <div class="f-track__actions" id="track-actions"> <!-- this is the modal !-->
        <div class="close-actions"> <!-- this is the close button !-->
            <span></span>
        </div>
        <div class="f-track-actions-inner">
            <!-- modal contents !-->
        </div>
    </div>
</div>

答案 1 :(得分:0)

好的,由于你的多个退出点在一个函数中发生了错误。让我指出你在哪里。

int readMatrix(int ***arrMatrices, int *noOfMatrices, int *heights, int *widths){
    int height, width;
    int output = scanf("%d %d",&height, &width);

    ...

    int **matrix = (int**) malloc(height * sizeof(int*));

    for(int i = 0; i < height; i++){
        int *row = (int*) malloc(width * sizeof(int));
        for(int y = 0; y < width; y++){
            output = scanf("%d",(row+y));
            if(output < 1) return -1; // <---- this is the problematic line
        }
        matrix[i] = row;
    }

    arrMatrices[*noOfMatrices] = matrix;

    heights[*noOfMatrices] = height;
    widths[*noOfMatrices] = width;
    *noOfMatrices+=1;

    return 1;
}

问题来自 if(输出&lt; 1)返回-1; 的readMatrix函数。想想如果代码在这一点退出时实际发生了什么?未释放为 int * row 分配的内存,这会导致内存泄漏问题。

解决方法是在返回-1之前需要释放行。 在软件工程中,强烈建议不要使用多个出口点。

推荐的代码更改:

int readMatrix(int ***arrMatrices, int *noOfMatrices, int *heights, int *widths){
    int result = 1;
    int height, width;
    int output = scanf("%d %d",&height, &width);

    ...

    int **matrix = (int**) malloc(height * sizeof(int*));

    for(int i = 0; i < height && result; i++){
       int *row = (int*) malloc(width * sizeof(int));
       for(int y = 0; y < width && result; y++){
           output = scanf("%d",(row+y));
           if(output < 1) result =0; // <---- this is the problematic line
       }
       if(result) matrix[i] = row;
       else free(row);
    }

    arrMatrices[*noOfMatrices] = matrix;

    heights[*noOfMatrices] = height;
    widths[*noOfMatrices] = width;
    *noOfMatrices+=1;

    return result;
}

那么这与你所看到的段错有什么关系?基于我看到的代码,您实际上已经预先初始化了行数。现在因为原始代码中的return语句,所有行都被分配了一个内存位置。因此,在代码的其他部分中,您尝试访问具有无效内存位置的行,这会导致段错误。所以你可能想看一下这部分代码。 =)