C - 内存泄漏故障排除

C - Memory leak troubleshooting

所以我正在编写一个代码来读取矩阵,然后对它们进行一些计数。

我分配内存来存储矩阵并用更多分配的 数组填充它 最后释放所有东西,但是 Valgrind 告诉我当我分配一个内存然后不释放它 但是 当我释放它时,程序不工作并且我得到 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);
}

所以问题是我读取矩阵,将其加载到我的数组中,然后 return。如果我尝试释放它,我会得到一个错误,并且显然最后全部释放它 - 逐行和矩阵逐个矩阵跟随我的整个数组 - 是不够的。 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)

好的,由于您在一个函数中有多个出口点,所以发生了这个错误。让我指给你看它在哪里。

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(output < 1) return -1; 处的 readMatrix 函数。想想如果此时代码退出,实际会发生什么?为 int *row 分配的内存未释放,这会导致内存泄漏问题。

解决方法是您需要在 returning -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 语句, 而不是 您的所有行都分配了内存位置。因此,在代码的其他部分,您尝试访问那些内存位置无效的行,这会导致段错误。所以你可能想检查代码的那些部分。 =)

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

您描述的问题似乎只有在为矩阵元素输入非数字值时才会出现。这使我认为您的意图是提供一种在发生错误时从矩阵中转义并再次输入该矩阵的值的方法。

正如@Mox 所指出的,您未能在此处释放一些内存。我不完全清楚你的段错误是如何产生的,我无法复制这种行为。

我在 readMatrix() 中做了一些更改。当遇到非数字输入时(例如 'q'),与当前 matrix 关联的所有分配必须是 freed。当然,当前的rowmatrix肯定是freed,但是你还得free你已经存储在[=中的rows 19=]。循环完成此操作,并且必须在 freeing matrix 之前完成。不需要清除输入流,因为在非数字输入的情况下程序会出错退出。最后,-1returned 到调用函数。

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;
}

这里还有另一个内存泄漏源。每个错误退出点必须在 returning:

之前释放所有 malloced 内存
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;        
    }

进行了这些更改后,我发现没有进一步的泄漏,Valgrind 同意:

输入正确:

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)

非数字输入:

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() 函数改回其原始形式(我认为!)。我认为您遇到的问题之一是您将@Mox 提供的解决方案的元素与我的原始解决方案的元素结合在一起。这两种解决方案都无法处理完整的图片,组合只会混淆事情。

您的 multiply() 功能似乎也有问题。在这里你分配了一个新的 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;
}