分配数组并释放它们的内存:Malloc和Free?

5

我目前在研究C语言,对指针使用和语法方面遇到了一些问题。

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

在创建完数组后,我使用for循环遍历每个元素并赋值。

现在,所有这些似乎都在工作,但是当我使用free()来释放内存时,程序会崩溃。

我发现如果我先用malloc()分配数组内存,然后立即调用free(),程序就能顺利执行。然而,如果我先malloc(),再赋值,然后再调用free(),程序就会崩溃(Segmentation fault)。

以下是可以运行的代码,其中使用了malloc()并立即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");
}

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()函数失败。

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

谢谢。


3
int *ptrArr[5] 声明了一个包含 5 个指针的数组。 - Eugene Sh.
我认为第一段代码不起作用。尝试将其大小调整为5或更大,甚至尝试10000。 - terence hill
4个回答

2

我解决这种问题的方法是打印出各个可疑对象的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个整数的数组,而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 );

0

这真是一个头疼的解码过程,但就像一个好的加密填字游戏一样,问题有解决方案。我稍微整理了一下。根本问题在于解引用。

请参见: 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]);
    }
}

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

这段话表明ptrArr[i]是指向一些整数的指针。

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

这句话的意思是ptrArr[i][j]是一个指向整数的指针。

它到底是什么?这两段代码在间接层数上有所不同。


0

你的第一个例子是不正确的,因为你打印了错误的数组索引。

嵌套的for循环调用了这个:

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

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

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

您可以看到int(* ptrArr [5])[5];实际上是指向5个整数数组的指针数组。而ptrArr [i]是指向5个整数数组的指针。

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

...

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

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

(这只是针对您的第一个示例的修复!我甚至还没有解决第二个示例。但解决方案大多相同。)


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