分配多维数组后内存泄漏

Memory leak after allocating multidimensional array

我写了一个简单的程序来读取任何方阵并计算它的行​​列式。但是,根据 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;
}

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

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

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

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

您在 main()

中为 matrix 分配内存
matrix = allocate_matrix(matrix, MATRIX);
N = parse_into(matrix);

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

matrix = allocate_matrix(matrix, N);

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

另外,请注意:

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

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

的大小

你的代码有很多问题。

首先,您没有正确初始化传递给 realloc 的指针。您必须确保它们都是有效的或 NULL。此处和多个地方的情况并非如此。

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

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

中的 sizeof(matrix[i]) 相同

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

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

再次 parse_into,你永远不会释放 first_row