Malloc的阵列,然后免费提供它们?

时间:2015-11-11 21:14:03

标签: c pointers memory-management segmentation-fault malloc

我目前正在乱搞c我在指针使用和语法区域有一些打嗝。

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

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

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

我注意到如果我在malloc()数组内存然后立即调用它们上的free(),程序执行没有问题。然而,当我malloc(),赋值和THEN调用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]);

以上按照我的预期执行,但是当我为单元格分配值然后尝试自由调用时,会生成分段错误:

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()失败的内存。

任何帮助都将不胜感激,我希望这个问题清晰简洁。

感谢。

4 个答案:

答案 0 :(得分: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 1}}。我们得出结论,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 );

答案 1 :(得分:0)

这很难解码,但就像一个好的神秘填字游戏,有一个解决方案。我已经整理了一些东西。根本问题是解除引用。

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

答案 2 :(得分: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]是指向整数的指针。

这是什么?这两段代码的间接层数不同。

答案 3 :(得分: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个int数组的指针数组。 ptrArr[i]是一个指向5个整数数组的指针。

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

...

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

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

(这只是你第一个例子的修复!我甚至没有解决第二个问题。但解决方案大致相同。)