【问题标题】:C - Memory leak troubleshootingC - 内存泄漏故障排除
【发布时间】:2016-11-18 23:35:32
【问题描述】:

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

我分配内存来存储矩阵并用更多分配的 数组 填充它并最终释放所有内容,但 Valgrind 告诉我,当我分配内存时我有内存泄漏,然后不要' t 释放它但是当我释放它时,程序不起作用,我得到 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 中的矩阵alloc。

谁能告诉我正确的做法?

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

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

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

EDIT 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)

【问题讨论】:

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


【解决方案1】:

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

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

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

我对@9​​87654322@ 做了一些更改。当遇到非数字输入时(例如,'q'),与当前matrix 关联的所有分配必须是freed。当然,当前的rowmatrix必须是freed,但是你还得free你已经存储在matrix中的rows。循环完成了这一点,这必须在freeing matrix 之前完成。无需清除输入流,因为在非数字输入的情况下程序会以错误退出。最后,-1return 分配给调用函数。

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

【讨论】:

  • 也许我很密集,但运行代码通常不会导致段错误或任何其他错误 - 我是否应该正确输入所有内容。只有 Valgrind 告诉我我没有足够的空闲时间。例如:我输入了 4 个矩阵,我将它们相乘或加/减,代码运行正常,但 Valgrind 说我释放的空间比我应该释放的少 4 倍,因为有 4 个矩阵。它指向readMatrix 中的那个alloc。我尝试使用您建议的编辑运行代码(不是结构,而是返回 -1 后的释放),但据我所知,它并没有改变任何事情。
  • 我认为在您的原始问题中,当您freed 分配时报告了段错误。我从来没有遇到任何段错误。我的答案中的代码运行,Valgrind 报告“所有堆块都被释放 - 没有泄漏是可能的。”您提供的代码不完整;它不允许输入 4 个矩阵,只有一个。如果您提供实际代码存在内存泄漏的示例(并且您发布的代码确实存在内存泄漏),那么也许我们可以修复它。
  • 1) 当我返回 -1 时,整个程序(从主程序)returns 100 作为错误并将错误打印到标准错误中。 2) 我不认为发布所有 180 行代码会有用,尽管我想我可以。但如果它有帮助 - 从 main 我调用 readMatrix 直到在矩阵 (+/-/*) 之后没有操作输入 → 直到 EOF。我将所有矩阵保存到 readMatrix 中的 arrOfMatrices 中,唯一一次我分配除 mainreadMatrix 中的内容之外的任何内容是在 multiply 中,在 Valgrind 中没有问题。
  • 这就是您需要提供minimal reproducible example 的原因。如果这是使问题明显的原因,则可以发布整个程序。我推测-1 的目的不是让用户重新输入矩阵,而是表示遇到+-* 之一?这些信息确实改变了一些事情。你说multiply()没有问题,但是freed这个分配怎么样?我们可能需要看到整个事情。
  • -1 是告诉main 读取不成功,例如提供了一个字母而不是数字,并以return 值为100 退出程序。我将发布完整的代码和 Valgrind 消息,让我使用正确的输入运行它。
【解决方案2】:

好的,由于您在函数中有多个退出点,因此正在发生错误。让我给你指出那在哪里。

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 处的 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 语句,不是您的所有行都被分配了一个内存位置。因此,在代码的其他部分中,您尝试访问那些内存位置无效的行,这会导致段错误。因此,您可能想查看代码的那些部分。 =)

【讨论】:

  • 这如何解释 OP 报告的段错误?
  • 好吧,我尝试了您的建议,但没有帮助。再次 - Valgrind 专门指向函数中的另一个 alloc。致int **matrix = (int**) malloc(height * sizeof(int*));
  • @keashi,哦,好吧,我只能就我能看到的提供建议,我不能告诉你没有向我展示的答案。我希望你知道我在这里的局限性。
  • @Mox 我稍微更新了问题,但我不知道还能提供什么
  • @Mox-- Valgrind 报告您的代码错误,因为当非数字输入中止输入时,一些内存未初始化。当条目被中止时,我们无法判断 OP 想要做什么。在我的解决方案中,这样的中止再次开始输入,因此所有矩阵行最终都被初始化。在您的解决方案中,某些行可能未初始化,即使已为它们分配空间。解决您的解决方案的最简单方法是将matrix 分配中的malloc 更改为calloc(height, sizeof(int*))
猜你喜欢
  • 2010-10-17
  • 1970-01-01
  • 1970-01-01
  • 2011-02-28
  • 1970-01-01
  • 1970-01-01
  • 2016-01-27
  • 2010-11-11
  • 2017-02-18
相关资源
最近更新 更多