【问题标题】:Malloc'ing Arrays and then Free'ing them?Malloc'ing Arrays 然后释放它们?
【发布时间】:2016-02-13 02:24:20
【问题描述】:

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

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

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

现在,所有这些似乎都在工作,但是当涉及到使用 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() 失败。

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

谢谢。

【问题讨论】:

  • int *ptrArr[5] 正在声明由 5 个指针组成的数组。
  • 我认为第一个代码不起作用。试试大于 5 的尺寸,也许是 10000

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


【解决方案1】:

我处理这类问题的方法是打印出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 个 int 组成的数组。

如果我添加以下代码行

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 );

【讨论】:

    【解决方案2】:
    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】:

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

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

      【讨论】:

        【解决方案4】:

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

        嵌套的 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 个整数数组的指针。

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

        ...

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-03-04
          • 1970-01-01
          • 2021-06-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多