【问题标题】:Memory leak after allocating multidimensional array分配多维数组后的内存泄漏
【发布时间】:2015-06-08 13:10:36
【问题描述】:

我编写了一个简单的程序来读取任何方阵并计算其行列式。然而,根据 Valgrind 的说法,它似乎正在泄漏内存。

示例会话:

./det
4 23 4
2 -5 2
45 2 40
330.000000

这是 Valgrind 的输出:

valgrind --leak-check=full --track-origins=yes ./det                                                                                                  ⏎
==5586== Memcheck, a memory error detector
==5586== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==5586== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==5586== Command: ./det
==5586== 
4 23 4
==5586== Conditional jump or move depends on uninitialised value(s)
==5586==    at 0x4C2D1CA: strcat (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x400EE6: readline (det.c:132)
==5586==    by 0x400B13: parse_into (det.c:53)
==5586==    by 0x40084D: main (det.c:20)
==5586==  Uninitialised value was created by a heap allocation
==5586==    at 0x4C29F90: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x4C2C33F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x400EB4: readline (det.c:127)
==5586==    by 0x400B13: parse_into (det.c:53)
==5586==    by 0x40084D: main (det.c:20)
==5586== 
2 -5 2
==5586== Conditional jump or move depends on uninitialised value(s)
==5586==    at 0x4C2D1CA: strcat (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x400EE6: readline (det.c:132)
==5586==    by 0x400C66: parse_into (det.c:79)
==5586==    by 0x40084D: main (det.c:20)
==5586==  Uninitialised value was created by a heap allocation
==5586==    at 0x4C29F90: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x4C2C33F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x400EB4: readline (det.c:127)
==5586==    by 0x400C66: parse_into (det.c:79)
==5586==    by 0x40084D: main (det.c:20)
==5586== 
45 2 40
==5586== Invalid read of size 8
==5586==    at 0x400929: determinant (det.c:37)
==5586==    by 0x400864: main (det.c:21)
==5586==  Address 0x51d9040 is 0 bytes inside a block of size 64 free'd
==5586==    at 0x4C2C29E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x400D39: allocate_matrix (det.c:95)
==5586==    by 0x400BFB: parse_into (det.c:72)
==5586==    by 0x40084D: main (det.c:20)
==5586== 
==5586== Conditional jump or move depends on uninitialised value(s)
==5586==    at 0x400945: determinant (det.c:37)
==5586==    by 0x400864: main (det.c:21)
==5586==  Uninitialised value was created by a heap allocation
==5586==    at 0x4C29F90: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x4C2C33F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x400DBD: allocate_matrix (det.c:102)
==5586==    by 0x40083D: main (det.c:19)
==5586== 
==5586== Conditional jump or move depends on uninitialised value(s)
==5586==    at 0x40094F: determinant (det.c:37)
==5586==    by 0x400864: main (det.c:21)
==5586==  Uninitialised value was created by a heap allocation
==5586==    at 0x4C29F90: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x4C2C33F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x400DBD: allocate_matrix (det.c:102)
==5586==    by 0x40083D: main (det.c:19)
==5586== 
0.000000
==5586== Invalid read of size 8
==5586==    at 0x400E24: free_matrix (det.c:113)
==5586==    by 0x40087F: main (det.c:22)
==5586==  Address 0x51d9040 is 0 bytes inside a block of size 64 free'd
==5586==    at 0x4C2C29E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x400D39: allocate_matrix (det.c:95)
==5586==    by 0x400BFB: parse_into (det.c:72)
==5586==    by 0x40084D: main (det.c:20)
==5586== 
==5586== Invalid free() / delete / delete[] / realloc()
==5586==    at 0x4C2B200: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x400E4B: free_matrix (det.c:114)
==5586==    by 0x40087F: main (det.c:22)
==5586==  Address 0x51d9040 is 0 bytes inside a block of size 64 free'd
==5586==    at 0x4C2C29E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x400D39: allocate_matrix (det.c:95)
==5586==    by 0x400BFB: parse_into (det.c:72)
==5586==    by 0x40084D: main (det.c:20)
==5586== 
==5586== 
==5586== HEAP SUMMARY:
==5586==     in use at exit: 624 bytes in 14 blocks
==5586==   total heap usage: 17 allocs, 4 frees, 761 bytes allocated
==5586== 
==5586== 8 bytes in 1 blocks are definitely lost in loss record 1 of 6
==5586==    at 0x4C29F90: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x4C2C33F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x400EB4: readline (det.c:127)
==5586==    by 0x400B13: parse_into (det.c:53)
==5586==    by 0x40084D: main (det.c:20)
==5586== 
==5586== 8 bytes in 1 blocks are definitely lost in loss record 2 of 6
==5586==    at 0x4C29F90: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x4C2C33F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x400EB4: readline (det.c:127)
==5586==    by 0x400C66: parse_into (det.c:79)
==5586==    by 0x40084D: main (det.c:20)
==5586== 
==5586== 64 bytes in 1 blocks are definitely lost in loss record 3 of 6
==5586==    at 0x4C29F90: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x400B41: parse_into (det.c:59)
==5586==    by 0x40084D: main (det.c:20)
==5586== 
==5586== 96 (24 direct, 72 indirect) bytes in 1 blocks are definitely lost in loss record 5 of 6
==5586==    at 0x4C2C29E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x400D39: allocate_matrix (det.c:95)
==5586==    by 0x400BFB: parse_into (det.c:72)
==5586==    by 0x40084D: main (det.c:20)
==5586== 
==5586== 448 bytes in 7 blocks are definitely lost in loss record 6 of 6
==5586==    at 0x4C29F90: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x4C2C33F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5586==    by 0x400DBD: allocate_matrix (det.c:102)
==5586==    by 0x40083D: main (det.c:19)
==5586== 
==5586== LEAK SUMMARY:
==5586==    definitely lost: 552 bytes in 11 blocks
==5586==    indirectly lost: 72 bytes in 3 blocks
==5586==      possibly lost: 0 bytes in 0 blocks
==5586==    still reachable: 0 bytes in 0 blocks
==5586==         suppressed: 0 bytes in 0 blocks
==5586== 
==5586== For counts of detected and suppressed errors, rerun with: -v
==5586== ERROR SUMMARY: 13 errors from 12 contexts (suppressed: 0 from 0)

我不知道为什么,但是在 Valgrind 会话期间结果是错误的!它如何以相同的输入输出3300? Valgrind 错误是否可信?

这是完整的代码。如您所见,我在使用堆后总是调用free()

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

#define MATRIX 8
#define CHUNK 32

double determinant(double **, size_t);
size_t parse_into(double **);
double **allocate_matrix(double **, size_t);
void free_matrix(double **);
char *readline();


int main(int argc, char *argv[]) {
    double **matrix = NULL;
    size_t N;
    matrix = allocate_matrix(matrix, MATRIX);
    N = parse_into(matrix);
    printf("%lf\n", determinant(matrix, N));
    free_matrix(matrix);
    return 0;
}


double determinant(double **matrix, size_t side) {
    if (side == 1) {
        return matrix[0][0];
    } else if (side == 2) {
        return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];
    }

    // Make the matrix triangular
    int i, j, t, r = 1;
    for (j = 0; j < side; j++) {
        if (!matrix[j][j]) return 0;
        for (i = j + 1; i < side; i++) {
            double ratio = matrix[i][j] / matrix[j][j];
            for (t = 0; t < side; t++) {
                matrix[i][t] -= ratio * matrix[j][t];
            }
        }
    }
    for (i = 0; i < side; i++) {
        r *= matrix[i][i];
    }
    return r;
}


size_t parse_into(double **matrix) {
    char *row = readline();
    size_t t;
    size_t N = 0, M = 0;
    size_t i = 1, j = 0;

    int *first_row;
    if (!(first_row = malloc(MATRIX * sizeof(first_row)))) {
        puts("Could not allocate memory.");
        exit(EXIT_FAILURE);
    }
    char *number = strtok(row, " ");
    while (number) {
        if (N == MATRIX) {
            first_row = realloc(first_row, 2 * N * sizeof(first_row));
        }
        first_row[N++] = atoi(number);
        number = strtok(NULL, " ");
    }
    M = N;
    matrix = allocate_matrix(matrix, N);
    for (t = 0; t < N; t++) {
        matrix[0][t] = first_row[t];
    }

    while (--M) {
        j = 0;
        row = readline();
        char *number = strtok(row, " ");
        while (number) {
            matrix[i][j++] = atoi(number);
            number = strtok(NULL, " ");
        }
        i++;
    }
    free(row);
    return N;
}


double **allocate_matrix(double **matrix, size_t side) {
    size_t i;

    if (!(matrix = realloc(matrix, sizeof(*matrix) * side))) {
        puts("Could not allocate memory.");
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < side; i++) {
        matrix[i] = NULL;
        if (!(matrix[i] = realloc(matrix[i], sizeof(matrix[i]) * side))) {
            puts("Could not allocate memory.");
            exit(EXIT_FAILURE);
        }
    }
    return matrix;
}


void free_matrix(double **matrix) {
    size_t length = sizeof(matrix[0]) / sizeof(matrix[0][0]);
    while (length--) free(matrix[length]);
    free(matrix);
}


char *readline() {
    char *input = NULL;
    char tmpbuf[CHUNK];
    size_t inputlen = 0, tmplen = 0;

    do {
        fgets(tmpbuf, CHUNK, stdin);
        tmplen = strlen(tmpbuf);
        inputlen += tmplen;
        input = realloc(input, inputlen + 1);
        if (!input) {
            puts("Could not allocate memory.");
            exit(EXIT_FAILURE);
        }
        strcat(input, tmpbuf);
    } while (tmplen == CHUNK - 1 && tmpbuf[CHUNK - 2] != '\n');

    return input;
}

编辑这是正确且有效的代码,以防有人感兴趣: https://codereview.stackexchange.com/questions/85769/reading-a-matrix-and-computing-the-determinant

【问题讨论】:

  • 除了发布的答案之外,realloc 之前的语句 matrix[i] = NULL 调用 allocate_matrix 会在您重新分配时丢弃当前分配的行。您需要跟踪矩阵大小,然后如果大小增加,则仅将 new 行设置为 null,如果大小减小,则将行设置为空。
  • 您的代码还有其他问题。例如,想想如果realloc 调用失败会发生什么?当将它分配回您重新分配的指针时,您将失去原始指针。
  • @JoachimPileborg 为什么我要关注 realloc?我认为如果我的程序无法分配内存,它可能会崩溃,因为无论如何都会丢失。
  • 发布的代码没有完全编译。 main() 函数的第一行有两个警告。每个未使用的参数有 1 个警告:argc 和 argv。这些不是唯一的问题。建议在启用所有警告的情况下编译并修复警告。
  • 大多数人试图优雅地处理故障,专业地通常要求没有程序突然崩溃。内存不足可能不是realloc 失败的唯一原因。

标签: c arrays matrix multidimensional-array memory-leaks


【解决方案1】:

你知道你打电话给allocate_matrix 两次吗?一次在 main 函数中,然后再次在 parse_into 函数中。这样做的问题是parse_into 中完成的分配仅对matrix 参数的本地副本进行,此更改不会传递给调用函数。

这当然会导致内存泄漏,而且您读取的数据不会在您从main 函数分配的矩阵中。

要解决这个问题,您需要将matrix 通过引用(或至少模拟它)传递给parse_into,或者想出另一种方法将这个新矩阵传递回@987654329 @。

【讨论】:

  • allocate_matrix 的本地副本没有丢失,最后返回。
  • @ElderBug 我说的是parse_into,当parse_into函数返回时,在parse_into内部调用allocate_matrix的结果会丢失。
  • 对不起,我不明白。我已经承认我的代码存在的所有问题,但我找不到解决它们的方法。另外,不是通过引用传递双指针已经吗?
  • @rubik 我建议您在调试器中逐行执行pasre_into 函数,然后您很可能会看到matrix 的值因调用allocate_matrix 而发生变化,但是当您离开parse_into 时,您应该注意到main 函数中matrix 的值仍然是old 值。这是因为您没有通过引用传递matrix,为此您需要一个指向matrix 的指针,该指针的类型为double***,并且您将使用地址运算符&amp; 传递它,并且在您需要使用解引用运算符*的函数。
【解决方案2】:

你在main()中为matrix分配内存

matrix = allocate_matrix(matrix, MATRIX);
N = parse_into(matrix);

但是在parse_into() 中,当你这样做时,你会丢弃该指针(及其内存)

matrix = allocate_matrix(matrix, N);

您还有一个潜在的错误。您通过将NULL 指针传递给realloc() 来分配内存。这将起作用,但是当您free() 内存时,您不会将指针重置为NULL。因此,如果您重新使用该指针,例如在另一次迭代中,realloc() 将失败。

【讨论】:

    【解决方案3】:

    另外,请注意:

    if (!(matrix[i] = realloc(matrix[i], sizeof(matrix[i]) * side)))
    

    错了。 sizeof(matrix[i]) 是指针的大小,但是这里你想要double的大小

    【讨论】:

      【解决方案4】:

      你的代码有很多问题。

      首先,您没有正确初始化传递给realloc 的指针。您必须确保它们都是有效的或为 NULL。这不是这里的情况,而且在很多地方。

      在您的 allocate_matrix 函数中,当您重新分配到较小的大小时,您将丢失指针。

      在您的free_matrix 函数中,sizeof(matrix[0]) 表示sizeof(double*),它是常量。这绝对不是你想要的。 sizeof(matrix[i])allocate_matrix 中也是如此。

      在您的 parse_into 中,您覆盖了 matrix 指针,但此更改在调用后会丢失,并且不会释放矩阵。

      在同一个函数中,您只调用一次free(row),尽管多次调用readlinereadline 将在每次调用时分配一个新缓冲区,因为 input 在函数中设置为 NULL。

      再次在parse_into,你永远不会免费first_row

      【讨论】:

      • 好的,感谢您的评价。我修复了最后两个疏忽问题。但是,我不知道如何解决前四个问题。在我看来,这个版本有很多问题,从头开始重写会更方便allocate_matrix。你有什么建议?
      • @rubik 也许你应该定义一个结构矩阵,用int 表示大小,double**。然后将 Matrix* 传递给您的所有函数。简而言之,像 C++ 类一样定义和使用 Matrix。并在您的函数中强制使用正确的 realloc。正确初始化指向 NULL 的指针。不要忘记使用 realloc 减少数组的副作用(你会丢失数据,之前释放它)。
      猜你喜欢
      • 2016-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-12
      • 1970-01-01
      相关资源
      最近更新 更多