C - 内存泄漏故障排除

标签 c matrix memory-management memory-leaks valgrind

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

我分配内存来存储矩阵并用更多分配的数组填充它并在最后释放所有内容,但是 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);
}

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

最佳答案

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

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

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

我在 readMatrix() 中做了一些改动。当遇到非数字输入时(例如,'q'),与当前矩阵相关的所有分配都必须是freed。当然,当前的 rowmatrix 必须是 freed,但是你也必须 free 您已经存储在 matrix 中的行。循环实现了这一点,而且这必须在释放ing matrix 之前完成。不需要清除输入流,因为在非数字输入的情况下程序会出错退出。最后,-1返回调用函数。

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[] 数组。您的函数原型(prototype)也将得到简化:

int readMatrix(struct Matrix *matrices, int *noOfMatrices);
void freeEverything(struct Matrix *matrices, int noOfMatrices, char *operations);

编辑

当您提供完整的代码时,您应该提供原始代码,而不是根据我们一直在做的错误假设进行更改的代码。但那没关系;在修复问题之前,我将 readMatrix() 函数改回其原始形式(我认为!)。我认为您遇到的问题之一是您将@Mox 提供的解决方案的元素与我的原始解决方案的元素结合在一起。这两种解决方案都无法应对全貌,这种组合只会让事情变得困惑。

您的 multiply() 函数似乎也有问题。在这里,您分配一个新的 matrix 来保存乘法的结果,然后用这个新矩阵替换 arrOfMatrices[] 中的矩阵。但是你必须在替换它之前释放旧的,否则你会泄漏内存,因为你失去了对它的引用。以下是如何更改 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;
}

关于C - 内存泄漏故障排除,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40687542/

相关文章:

c - C语言中的 "Dummy for Loop"是什么?

R/从下三角形元素的向量中创建一个对称矩阵

python - 使用 numpy 将矩阵高对角线归零

iphone - NSThread 有单独的堆吗? pthread 怎么样(在 iPhone 上)

c - 结构数组 - C 中的内存释放

android - 我们如何使用 USER-AGENT 字符串来区分 1.笔记本电脑/台式机 2.IOS 3. Android 4.其他手机(BB、诺基亚、MS Mobile)

c - execl() 适用于我的一个代码,但不适用于另一代码

c++ - 如何禁用 Dev C++ 中的提示?

opencv - PCA如何用于SIFT或VLAD载体?

c++ - 覆盖标准库使用的内存分配方法?