Malloc'ing 数组然后释放它们?

Malloc'ing Arrays and then Free'ing them?

我目前正在处理 c 我在指针使用和语法方面遇到了一些问题。

下面,我(尝试)创建一个指向整数数组的指针数组,然后将每个指针指向通过 malloc() 创建的数组。

创建数组后,我会循环遍历每个单元格,并分配一个值。

现在,所有这些似乎都有效,但是当使用 free() 收回内存时,程序崩溃了。

我注意到,如果我 malloc() 数组内存,然后立即对它们调用 free(),程序将毫无问题地执行。但是,当我 malloc()、分配值然后调用 free() 时,将发生崩溃。 (分段错误)。

下面是有效的代码,malloc() 然后立即 free()ing

int (*ptrArr[5])[5];

for(int i=0; i<5; i++)
    ptrArr[i] = malloc(sizeof(int) * 5);

for(int i=0; i<5; i++)
    printf("ptrArr[%d][%x]->[%x]\n", i, &ptrArr[i], &*ptrArr[i]);
printf("\n");

for(int i=0; i<5; i++){
    for(int j=0; j<5; j++){
        printf("[%x](%2d) | ", &*ptrArr[i][j], *ptrArr[i][j]);  
    }
    printf("\n");
}

for(int i=4; i>=0; i--)
    free(ptrArr[i]);

上面的代码按我的预期执行,但是当我为单元格赋值然后尝试调用 free 时,生成了分段错误:

int (*ptrArr[5])[5];

for(int i=0; i<5; i++)
    ptrArr[i] = malloc(sizeof(int) * 5);

for(int i=0; i<5; i++)
    printf("ptrArr[%d][%x]->[%x]\n", i, &ptrArr[i], &*ptrArr[i]);
printf("\n");

for(int i=0; i<5; i++){
    for(int j=0; j<5; j++){
        printf("[%x](%2d) | ", &*ptrArr[i][j], *ptrArr[i][j]);  
    }
    printf("\n");
}

int loop = 5;
for(int i=0; i<5; i++){
    for(int j=0; j<5; j++){
        *ptrArr[i][j] = (loop + (i*j));
        ++loop;
    }
}

printf("\n");
for(int i=0; i<5; i++){
    for(int j=0; j<5; j++){
        printf("[%x](%2d) | ", &*ptrArr[i][j], *ptrArr[i][j]);
    }
    printf("\n");
}

for(int i=4; i>=0; i--)
{
    printf("Freeing ptrArr[%x]\n", i);
    free(ptrArr[i]);
}

我想我要么误解了

int (*ptrArr[5])[5];

声明,我想要的是一个包含 5 个指针的数组,每个指针都指向一个整数数组,或者我没有正确地为单元格赋值,而是破坏了导致 free() 失败的内存。

如有任何帮助,我将不胜感激,我希望问题简明扼要。

谢谢。

嗯,解码起来很头疼,但就像一个很好的神秘填字游戏一样,有一个解决方案。我已经整理了一下。根本问题是取消引用。

参见: Order of operations for dereference and bracket-ref in C

我在下面的一些工作代码中标记了该行。我在 (*ptrArr[i]) 周围添加了括号以获得数组,然后可以将其正确索引为 (*ptrArr[i])[j]。

我还建议了另一种声明数组的方法。一个更容易验证的!

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

#define SIZE 5 

int main()
{
    // This makes things clearer :-
    typedef int ArrayType[SIZE];
    typedef ArrayType * ArrayPtr;
    ArrayPtr ptrArr[SIZE];

    for (int i = 0; i < SIZE; i++)
        ptrArr[i] = malloc (sizeof (int) * SIZE);

    int loop = SIZE;

    for (int i = 0; i < SIZE; i++)
    {
        for (int j = 0; j < SIZE; j++)
        {
            // THIS NEXT LINE HAD THE WRONG BRACKETS IN
            // *(ptrArr[i])[j] = (loop + (i * j));
            (*ptrArr[i])[j] = (loop + (i * j));
            printf("array has base address: %lx\n", ptrArr[i]); 
            printf("writing to: %lx\n", &(*ptrArr[i])[j]); 
            ++loop;
        }
    }

    for (int i = SIZE-1; i >= 0; i--)
    {
        printf ("Freeing ptrArr[%x] with address %lx\n", i, ptrArr[i]);
        free (ptrArr[i]);
    }
}
for(int i=0; i<5; i++)
    ptrArr[i] = malloc(sizeof(int) * 5);

这表示 ptrArr[i] 是指向某些整数的指针。

    *ptrArr[i][j] = (loop + (i*j));

这表示 ptrArr[i][j] 是一个指向整数的指针。

这是哪个?这两段代码的区别在于间接层数。

您的第一个示例不正确,因为您打印的数组索引不正确。

嵌套的for循环调用这个:

printf("[%x](%2d) | ", &*ptrArr[i][j], *ptrArr[i][j]); 

第三个参数与ptrArr[i][j][0]相同。

但这是不正确的。它应该是 ptrArr[i][0][j] 因为你用 malloc 调用只分配了二维数组的一个内部维度:ptrArr[i] = malloc(sizeof(int) * 5);.

你看int (*ptrArr[5])[5];实际上是一个指向5个整数数组的指针数组。 ptrArr[i] 是指向 5 个整数的数组的指针。

这也意味着与 ptrArr[i][j] 相同的第二个参数 &*ptrArr[i][j] 实际上应该是 ptrArr[i][0],因为同样,我们只分配了一个维度。

...

我们快完成了。首先用 calloc 替换 malloc 调用,这样我们就不会打印垃圾值。然后将 %x printf 说明符替换为 %p 并将它们各自的参数转换为 (void*).

通过这些更正,第一个示例打印出正确的地址和正确的值,并且没有未定义的行为。(您还可以删除多余的 &* 对)

(这只是对第一个示例的修复!我什至没有解决第二个示例。但解决方案基本相同。)

我处理这类问题的方法是打印出 sizeof 各种嫌疑人,就像这样

int (*test[5])[5];

printf( "%zu ", sizeof(test) );
printf( "%zu ", sizeof(test[0]) );
printf( "%zu\n", sizeof(test[0][0]) );

结果是40 8 20。 (请注意,在我的机器上,int 是 4 个字节,指针是 8 个字节。)
所以这告诉我 test 是一个包含 5 个指针的数组。从逻辑上讲,test[0] 是一个指针。但有趣的是 test[0][0] 是一个包含 5 个整数的数组。

如果我添加下面这行代码

printf( "%zu\n", sizeof(test[0][0][0]) );

输出为4,即test[0][0][0]是单个int。由此我们得出结论,声明 int (*test[5])[5] 正在声明一个三维数组,这不是您想要的。


所以让我们尝试一个更简单的声明,像这样

int (*test)[5];

printf( "%zu ", sizeof(test) );
printf( "%zu ", sizeof(test[0]) );
printf( "%zu\n", sizeof(test[0][0]) );

输出的是8 20 4,也就是说test是一个单指针,test[0]是一个5个int的数组,test[0][0]是一个单指针int。我们得出结论,int (*test)[5]声明了一个二维数组。


接下来的问题是我们如何为数组分配内存。如果我们这样做

test = malloc( 5 * sizeof(int) );

然后我们就有了一个1行5列的数组,基本上是一个一维数组。

要得到一个有N行的二维数组,我们需要

test = malloc( N * 5 * sizeof(int) );

然后我们可以像这样填充、打印和释放数组

int N = 5;
for ( int row = 0; row < N; row++ )
    for ( int col = 0; col < 5; col++ )
        test[row][col] = (row+5)*10 + col;

for ( int row = 0; row < N; row++ )
{
    for ( int col = 0; col < 5; col++ )
        printf( "%2d ", test[row][col] );
    printf( "\n" );
}

free( test );