C语言中使用指针的整数矩阵——内存分配混淆

6

我在生成int矩阵时遇到了一些问题,不能创建内存泄漏。我想通过read_matrix()动态地使给定的(全局)矩阵变成任意大小。但是后来我想释放内存。因此,在我的主方法中,第二个printf应该会导致总线错误,因为它不应该有任何分配给它的内存。我该如何创建这个?

int**       first_matrix;
int**       second_matrix;
int**       result_matrix;

int** read_matrix(int size_x, int size_y)
{
    int** matrix;
    matrix = calloc(size_x, sizeof(int*));
    for(int i = 0;i<size_x;i++) {
        matrix[i] = calloc(size_y, sizeof(int));
    }
    for(int i = 0;i<size_x;i++) {
        for(int j = 0;j<size_y;j++) {
            matrix[i][j] = i*10+j;
        }
    }
    return matrix;
}

int main(int stackc, char** stack)
{
    first_matrix = read_matrix(10,10);
    printf("9:3 %d - 4:6 %d \n", first_matrix[9][3], first_matrix[4][6]);
    free(*first_matrix);
    free(first_matrix);
    printf("9:3 %d - 4:6 %d \n", first_matrix[9][3], first_matrix[4][6]);
}
7个回答

9

即使内存已被释放,也不代表您不能访问它!当然,在内存被释放后访问它是一个非常糟糕的主意,但这就是为什么它在您的示例中有效的原因。

请注意,free(*first_matrix)只会释放first_matrix[0],而不是其他数组。您可能需要一些标记来表示最后一个数组(除非您总是知道在释放外部数组时分配了多少个内部数组)。例如:

int** read_matrix(int size_x, int size_y)
{
    int** matrix;
    matrix = calloc(size_x, 1+sizeof(int*)); // alloc one extra ptr
    for(int i = 0;i<size_x;i++) {
        matrix[i] = calloc(size_y, sizeof(int));
    }
    matrix[size_x] = NULL; // set the extra ptr to NULL
    for(int i = 0;i<size_x;i++) {
        for(int j = 0;j<size_y;j++) {
            matrix[i][j] = i*10+j;
        }
    }
    return matrix;
}

当你释放它们时:

// keep looping until you find the NULL one
for( int i=0; first_matrix[i] != NULL; i++ ) {
    free( first_matrix[i] );
}
free( first_matrix );

似乎循环直到matrix[i]!= NULL即使我没有将最后一个指针设置为NULL也可以工作,这是巧合还是其他不好的事情? - Fredrik
如果你分配了一个额外的指针并且没有设置它,它可能会默认为NULL,这取决于操作系统(Windows会将分配的内存清零,Unix则不会)。如果不是NULL,你将超出分配的数组末尾,并继续释放东西,直到你碰巧遇到一个NULL指针或者更可能的是崩溃。 - Graeme Perrow
1
calloc总是将内存设置为null。 - quinmars

2

您需要逐行释放:


void free_matrix(int **matrix, int size_x)
{
    for(int i = 0; i < size_x; i++)
        free(matrix[i]);
    free(matrix);
}

在C语言中,你不能在for循环中声明变量。 - terminus

1

释放内存并不意味着它消失了,只是意味着另一个分配可能会抓住同一块内存。无论你放什么在里面,直到其他东西覆盖它之前,它都还在那里。

此外,您没有释放您分配的所有内容。您只释放指针数组和第一行。但即使您正确释放了所有内容,您仍将具有相同的效果。

如果您想创建“总线错误”,则需要指向不属于您进程的内存。无论如何,您为什么要这样做?


我只是想这样做来展示内存已被释放,但现在意识到我的思维有误,无法运行Valgrind,因为我使用的是OS X,并且想找到一种方法来确定是否真正释放了它。 - Fredrik

0

我建议使用valgrind来追踪未释放的内存,而不是试图引发总线错误。它在许多其他方面也非常出色。

Sam


我使用的是OS X系统,所以无法运行Valgrind,否则我就会运行它了 :/ 你知道有什么在Mac上可用的替代品吗? - Fredrik

0

你在这里缺失的概念是,对于每个calloc,必须有一个free。 并且该free必须应用于从calloc返回的指针。

我建议您创建一个函数(命名为delete_matrix) 使用循环释放您在此处分配的所有指针

for(int i = 0;i < size_x;i++) { matrix[i] = calloc(size_y, sizeof(int)); }

然后,一旦完成,释放此分配的指针。

matrix = calloc(size_x, sizeof(int*));

您现在所做的方式,

free(*first_matrix); free(first_matrix);

不会实现您想要的效果。


0

你只释放了first_matrix的第一行(或第一列)。编写另一个类似于这样的函数:

void free_matrix(int **matrix, int rows)
{
    int i;
    for(i=0; i<rows; i++)
    {
        free(matrix[i]);
    }
    free(matrix);
}

你可能想把矩阵转换成一个结构体来存储它的行和列数。

0

你的内存泄漏问题是因为你释放了矩阵的第一行和行列表,但没有释放1到n行中的任何一行。你需要在循环中调用free函数。

然而,有几个替代方案: - 分配sizeof(int*)rows + rowscols*sizeof(int)字节,并使用前面的字节作为行指针。这样,你只需要释放单个内存块(对分配器来说也更容易) - 使用一个包含行数的结构体。然后你可以避免使用行列表(节省内存)。唯一的缺点是你必须使用函数、宏或一些混乱的符号来访问矩阵。

如果你选择第二个选项,在任何C99编译器中都可以使用像这样的结构体,而且只需要分配一个大小为numints*sizeof(int)+sizeof(int)的内存块:

struct matrix {
    int rows;
    int data[0];
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接