【问题标题】:Dynamic memory allocation in each iteration of for loop to make dynamic array在for循环的每次迭代中动态分配内存以制作动态数组
【发布时间】:2021-03-28 19:11:30
【问题描述】:

这段代码有什么问题?我试图在每次循环运行时分配 1 个 int 大小的内存,但是这段代码不打印任何东西

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int *a;
    for(int i=0;i<5;i++)
    {
        a=(int *)malloc(1*sizeof(int));
        a[i]=i;
    }
    for(int i=0;i<5;i++)
    {
        printf("%d\n",a[i]);
    }
    free(a);

}

【问题讨论】:

  • Alloc a 一次与总大小。如果您需要更改分配内存的大小,请使用realloc,始终使用总大小。

标签: arrays c dynamic malloc


【解决方案1】:

你需要做的是分别为每个整数分配内存,但你没有这样做。

您正在对同一变量 a 进行重复分配,这不是正确的做法,还会导致内存泄漏,因为您丢失了以前的指针值

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

int main()
{
    int *a[5] = {NULL};

    for(int i = 0; i < 5;i++)
    {
        a[i] = malloc(sizeof(int));
        if(a[i])
            *a[i] = i;
        else
            puts("cannot malloc");
    }
    
    for(int i = 0; i < 5; i++)
    {
        printf("addr = %p and value = %d\n",a[i],*a[i] );
        free(a[i]);
        a[i] = NULL;
    }
    return 0;
}

但是如果你只是想存储5个整数,那么上面的方法是多余的,而是一次为5个整数分配内存并使用它,如下所示。

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

完成后,您可以说free(a); a = NULL;

【讨论】:

    【解决方案2】:

    如果您想分配一维数组,请执行此操作

    #include<stdio.h>
    #include<stdlib.h>
    int main()
    {
        int *a = (int*)malloc(5*sizeof(int)); // allocate 5 int-items on your array 'a'
        for(int i=0;i<5;i++)
        {
            a[i]=i;
        }
        for(int i=0;i<5;i++)
        {
            printf("%d\n",a[i]);
        }
        free(a);
    }
    

    您不需要单独分配每个 a[i]。

    而且,我假设你会很难制作二维数组。

    如果你想这样做。试试这个

    #include<stdio.h>
    #include<stdlib.h>
    int main()
    { // make 5 * 5 array
        int **a = (int**)malloc(5*sizeof(int*));
        for(int i=0;i<5;i++)
        {
            a[i] = (int*)malloc(5*sizeof(int));
        }
    
        ... do some logics
    
        for(int i = 0; i < 5; i++) {
            free(a[i]); // you need to free each a[i] to prevent memory leak.
        }
        free(a); // finally, free a (int**)
    }
    

    【讨论】:

      【解决方案3】:

      如果您已经知道要分配的对象数量,那么您实际上并没有动态调整任何东西的大小,您只是分配一块内存来保存该数量的对象。

      为对象分配存储而不是仅仅创建一个数组时的区别在于对象集合的存储持续时间。声明一个普通的旧数组会导致数组具有自动存储持续时间(数组的有效生命周期仅限于声明它的范围)。使用malloccallocrealloc 分配的内存块具有分配的存储持续时间(在释放或程序退出之前有效)C11 Standard - 6.2.4 Storage durations of objects

      当您使用malloccallocrealloc 分配内存时,该函数会保留请求的字节数,并在成功时将起始地址返回到分配的内存块,在失败时返回NULL

      对于分配的内存块,您有 2 个职责:(1) 始终为内存块保留指向起始地址的指针,因此,(2) 它不再需要时可以释放。在您的代码中,您没有执行 (1)。当您将分配放在循环中时,例如

          a=(int *)malloc(1*sizeof(int));
      

      您将a 持有的地址覆盖为它的值——在每次迭代中。通过不保留指向每个内存块的指针,每次覆盖a 持有的地址时都会创建一个memory-leak,因为前一个内存块的地址丢失了,你不能free() 上面的内存块失败 (2)。

      (注意:在C中,malloc的return不用强制转换,没必要。见:Do I cast the result of malloc?)

      要解决您当前的问题,只需在进入循环之前进行分配,例如:

      #include <stdio.h>
      #include <stdlib.h>
      
      #define NOBJ 5          /* if you need a constant, #define one or more */
      
      int main (void) {
          
          int *a = malloc (NOBJ * sizeof *a);             /* allocate storage for n objects */
          
          if (a == NULL) {                                /* validate EVERY allocation */
              perror ("malloc-a");
              return 1;
          }
          
          for (int i = 0; i < NOBJ; i++)                  /* fill a */
              a[i] = i;
              
          for (int i = 0; i < NOBJ; i++)                  /* output a */
              printf ("a[%d]: %d\n", i, a[i]);
      
          free (a);                                       /* free allocated memory */
      }
      

      使用/输出示例

      $ ./bin/malloc-a
      a[0]: 0
      a[1]: 1
      a[2]: 2
      a[3]: 3
      a[4]: 4
      

      内存使用/错误检查

      您必须使用内存错误检查程序来确保您不会尝试访问内存或写入超出/超出分配块的边界,尝试读取或基于未初始化的值进行条件跳转,最后, 以确认您已释放所有已分配的内存。

      对于 Linux,valgrind 是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。

      $ valgrind ./bin/malloc-a
      ==5019== Memcheck, a memory error detector
      ==5019== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
      ==5019== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
      ==5019== Command: ./bin/malloc-a
      ==5019==
      a[0]: 0
      a[1]: 1
      a[2]: 2
      a[3]: 3
      a[4]: 4
      ==5019==
      ==5019== HEAP SUMMARY:
      ==5019==     in use at exit: 0 bytes in 0 blocks
      ==5019==   total heap usage: 2 allocs, 2 frees, 1,044 bytes allocated
      ==5019==
      ==5019== All heap blocks were freed -- no leaks are possible
      ==5019==
      ==5019== For counts of detected and suppressed errors, rerun with: -v
      ==5019== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
      

      始终确认您已释放已分配的所有内存并且没有内存错误。

      动态调整存储容量

      当您有 未知 数量的对象要存储时,您必须动态分配存储空间。该方法是直截了当的。您最初为一些合理预期的对象数量分配并保留两个计数器,一个用于当前可用分配的内存中可以容纳的对象数量,第二个计数器用于您分配的那些对象的数量使用过。当used == available* you must realloc` 调整内存块大小时,会创建更多可用存储空间。重复直到您用完要存储的对象。

      当您调用realloc() 时,您始终使用临时 指针realloc(),因此如果realloc() 未能返回NULL,您不会用@ 覆盖当前分配的块的地址987654344@ 创建内存泄漏。例如永远不要这样做:

              if (used == avail) {                        /* is reallocation needed? */
                  a = realloc (a, 2 * avail * sizeof *a);
      

      改为:

              if (used == avail) {                        /* is reallocation needed? */
                  /* always realloc to a temporary pointer */
                  void *tmp = realloc (a, 2 * avail * sizeof *a);
                  if (!tmp) {                             /* validate EVERY reallocation */
                      perror ("realloc-a");
                      break;
                  }
                  a = tmp;                                /* assigned resized block to a */
                  ...
      

      这样,(不是if),realloc() 失败时,您会在a 中保留指向原始内存块的指针。它仍然指向在调用realloc() 之前存储在a 中的有效数据。

      下面是一个简短的示例,它最初为2 整数值分配,然后在1050 之间生成一个随机数,并填充该数量的对象,根据需要重新分配存储空间。对于额外的未知数,如果随机对象的原始数量是奇数,则将另一个介于 1050 之间的随机数添加到原始数量并继续填充和重新分配,直到存储所有值,例如

      #include <stdio.h>
      #include <stdlib.h>
      #include <time.h>
      
      #define AVAIL  2        /* if you need a constant, #define one or more */
      #define OMAX  50        /* maximum no. of objects per-random increment */
      #define OMIN  10        /* minimum no. of objects per-random increment */
      
      int main (void) {
          
          size_t  used = 0,                               /* number of objects used */
                  avail = AVAIL,                          /* number available */
                  nrealloc = 0,                           /* number of reallocations */
                  nobjects;                               /* random no. of objects to fill */
          int *a = malloc (avail * sizeof *a);            /* allocate storage for n objects */
          
          if (a == NULL) {                                /* validate EVERY allocation */
              perror ("malloc-a");
              return 1;
          }
          
          srand (time(NULL));                             /* seed random no. generator */
          nobjects = rand() % (OMAX - OMIN + 1) + OMIN;   /* random no 10 - 50 */
          
          while (used < nobjects) {                       /* loop until a contains nobjects */
              if (used == avail) {                        /* is reallocation needed? */
                  /* always realloc to a temporary pointer */
                  void *tmp = realloc (a, 2 * avail * sizeof *a);
                  if (!tmp) {                             /* validate EVERY reallocation */
                      perror ("realloc-a");
                      break;
                  }
                  a = tmp;                                /* assigned resized block to a */
                  avail *= 2;                             /* update available objects */
                  nrealloc += 1;                          /* add to no. of reallocations */
              }
              a[used] = used;                             /* assign value */
              used++;                                     /* increment used count */
          }
          
          if (used & 1) {     /* if odd number used, add to nobjects */
              size_t group1 = nobjects;
              nobjects += rand() % (OMAX - OMIN + 1) + OMIN;
              printf ("\nnobjects increased from %zu - %zu\n\n", group1, nobjects);
          }
          
          while (used < nobjects) {                       /* loop until a contains nobjects */
              if (used == avail) {                        /* is reallocation needed? */
                  /* always realloc to a temporary pointer */
                  void *tmp = realloc (a, 2 * avail * sizeof *a);
                  if (!tmp) {                             /* validate EVERY reallocation */
                      perror ("realloc-a");
                      break;
                  }
                  a = tmp;                                /* assigned resized block to a */
                  avail *= 2;                             /* update available objects */
                  nrealloc += 1;                          /* add to no. of reallocations */
              }
              a[used] = used;                             /* assign value */
              used++;                                     /* increment used count */
          }
          
          /* results */
          printf ("allocations   : 1\nreallocations : %zu\n\n", nrealloc);
          for (size_t i = 0; i < used; i++)               /* output a */
              printf ("a[%3zu]: %d\n", i, a[i]);
      
          free (a);                                       /* free allocated memory */
      }
      

      使用/输出示例

      注意:对象的原始数量是19(奇数),因此第二个随机对象块被分配并添加到a

      $ ./bin/malloc-realloc-a
      
      nobjects increased from 19 - 44
      
      allocations   : 1
      reallocations : 5
      
      a[  0]: 0
      a[  1]: 1
      a[  2]: 2
      a[  3]: 3
      a[  4]: 4
      a[  5]: 5
      a[  6]: 6
      a[  7]: 7
      a[  8]: 8
      a[  9]: 9
      a[ 10]: 10
      ...
      a[ 40]: 40
      a[ 41]: 41
      a[ 42]: 42
      a[ 43]: 43
      

      当您realloc() 时,您可以选择任何数量或因子的附加对象来分配,但是将当前大小加倍很方便,并且可以很好地平衡增加存储空间与需要realloc() 的次数。例如,您可以在 20 次重新分配中从最初分配的 2 个对象增加到超过 2,000,000 个。您还可以最后一次调用realloc() 并使用总数used 将总分配缩小到所需的确切大小。

      查看一下,如果您还有其他问题,请告诉我。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-02-27
        • 1970-01-01
        • 1970-01-01
        • 2018-02-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-02
        相关资源
        最近更新 更多