【问题标题】:C - How to dynamically allocate memory for each array element?C - 如何为每个数组元素动态分配内存?
【发布时间】:2018-12-28 07:45:48
【问题描述】:

我有以下结构

typedef struct h{
        int key;
        float data;
        char name[20];
}heaparr;

我想为每个元素动态分配内存

heaparr *heap;

为了给每个元素动态分配内存,我用过

heap[i]=(heaparr*)malloc(sizeof(heaparr));

但是每次我编译代码时,我都会得到一个赋值类型不匹配的错误。我该如何解决这种情况?提前致谢。

【问题讨论】:

  • 你很困惑。 heaparr *head; 声明了一个 single 指针,您可以向其分配 1 块内存。如果您想单独分配,请使用 heaparr **head; 并分配一些指针,例如head = malloc (somenum * sizeof *head);,然后你可以为每个指针分配head[i] = malloc (sizeof *head[i]);(但不要分配更多的somenum-1,除非你先realloc更多的指针。
  • 或者您可以使用固定数量的指针并按照@TheCrow 下面的说明单独分配(但请注意——您仅限于该固定数量)。
  • 了解flexible array members。它们可能与您的问题相关

标签: c malloc heap-memory dynamic-memory-allocation


【解决方案1】:

只能通过指针动态分配。如果要动态分配数组的每个元素,那么每个元素都必须是一个指针。

[编辑]感谢@David C. Rankin 指出我,在这个特定的示例中,我在堆栈中声明了一个包含 10 个指针的数组,以制作代码更简单。您可以创建包含任意数量元素的预定义大小的数组,但它有一个限制,即一旦达到限制,您就不能realloc 堆数据。您始终可以动态创建数组。

#include <stdlib.h>

typedef struct h {
    int key;
    float data;
    char name[20];
} heaparr;

int main()
{
    heaparr *heap[10];
    int i;
    for (i = 0; i < 10; ++i) {
        heap[0] = malloc(sizeof(heaparr));
    }

    return 0;
}

【讨论】:

  • 好的,已编辑。我只是想遵循问题的代码流。谢谢。
  • 当然,您可能还想明确声明您声明了一个指针数组 (heaparr *[10]),因此您仅限于10-pointers,不能realloc@达到限制后 987654326@。 (完全有效的方式,但它有这个限制)
【解决方案2】:

作为 TheCrow 答案的后续,如果您确实想以一种允许您处理未知数量的结构的方式为每个 heap[i] 分配,那么您可以使用 pointer-to -指向heaparr(例如heaparr **heap;)的指针,然后最初分配一些指针,并在从输入中读取每个结构时为其分配。

该方案很简单。您保留两个计数器变量。一个代表分配的指针数量,第二个代表使用的数量。当使用的指针数量等于分配的数量时,您只需 realloc 更多指针,然后再尝试分配/填充下一个指针。

下面是一个简单的例子,它从stdin 读取数据并期望每一行都包含"key data name"。最初分配了2 指针,并根据需要通过将当前分配的所有指针都填满时分配的当前数量加倍来重新分配指针的数量,例如

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

/* if you need constants, #define them or use a global enum */
enum { NPTR = 2, MAXNM = 20, MAXC = 128 };

typedef struct {
    int key;
    float data;
    char name[MAXNM];
} heaparr;

int main (void) {

    char buf[MAXC] = "";    /* read buffer */
    size_t  n = 0,          /* current struct filled */
            nptr = NPTR;    /* initial/current number of pointers */
    heaparr **heap = NULL;  /* pointer-to-pointer to heaparr */

    /* allocate/validate nptr to heap */
    if (!(heap = malloc (nptr * sizeof *heap))) {
        perror ("malloc-heap");
        return 1;
    }

    while (fgets (buf, MAXC, stdin)) {          /* read each line */
        heaparr tmp = { .key = 0 };             /* tmp struct */
        if (sscanf (buf, "%d %f %19[^'\n']",    /* parse line/validate */
                    &tmp.key, &tmp.data, tmp.name) != 3) {
            fprintf (stderr, "error: failed conversion line %zu.\n", n);
            continue;       /* just read next line on conversion failure */
        }
        if (n == nptr) {    /* check if realloc needed */
            /* always relloc to a temporary pointer to avoid mem-leak */
            void *tmpheap = realloc (heap, nptr * 2 * sizeof *heap);
            if (!tmpheap) { /* validate realloc */
                perror ("realloc-tmpheap");
                break;  /* don't exit, original data still valid in heap */
            }
            heap = tmpheap; /* assign new block to heap */
            nptr *= 2;      /* update current pointers allocated */
        }
        if (!(heap[n] = malloc (sizeof *heap[n]))) { /* allocate heap[n] */
            perror ("malloc-heap[n]");
            break;
        }
        *heap[n++] = tmp;   /* assign tmp to heap[n], increment n */
    }

    for (size_t i = 0; i < n; i++) {    /* output all values */
        printf ("%3d %5.1f   %s\n", heap[i]->key, heap[i]->data, 
                                    heap[i]->name);
        free (heap[i]);     /* don't forget to free each struct */
    }
    free (heap);            /* don't forget to free pointers */

    return 0;
}

(下面读取 4 个结构体的数据需要上面的 realloc

输入文件示例

$ cat dat/intfloatstr.txt
1 1.1 my
2 2.2 dog
3 3.3 has
4 4.4 fleas

使用/输出示例

$ ./bin/dynallocstruct <dat/intfloatstr.txt
  1   1.1   my
  2   2.2   dog
  3   3.3   has
  4   4.4   fleas

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您都有 2 个职责:(1)始终保留指向起始地址的指针内存块,因此,(2) 当不再需要它时可以释放

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

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

$ valgrind ./bin/dynallocstruct <dat/intfloatstr.txt
==8846== Memcheck, a memory error detector
==8846== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==8846== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==8846== Command: ./bin/dynallocstruct
==8846==
  1   1.1   my
  2   2.2   dog
  3   3.3   has
  4   4.4   fleas
==8846==
==8846== HEAP SUMMARY:
==8846==     in use at exit: 0 bytes in 0 blocks
==8846==   total heap usage: 6 allocs, 6 frees, 160 bytes allocated
==8846==
==8846== All heap blocks were freed -- no leaks are possible
==8846==
==8846== For counts of detected and suppressed errors, rerun with: -v
==8846== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

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

【讨论】:

    【解决方案3】:

    您可以在指针的帮助下通过获取下标的大小为每个数组索引分配内存,并以所需的数组索引大小开始循环。

    也不需要对 malloc 的返回进行类型转换。 找到下面提到的代码sn-p:

    typedef struct h
    {    
     int key;
     float data;
     char name[20];
    }heaparr;
    
    int main()
    {
      int     size = 4;
      int     iLoop = 0;
      heaparr *heap[size];
    
      for (iLoop = 0; iLoop < size; iLoop++)
      {
        heap[iLoop] = malloc(sizeof(heaparr));
      } 
    return 0;
    }
    

    希望这能消除您的疑虑。

    【讨论】:

      【解决方案4】:

      我认为下面的代码适合你...

      int main
      {
          ///In Main Function you need to allocate the memory for structure format  
           heaparr *heap;
           int num,i; //How many structures you want it?
           printf("Enter the size");
           scanf("%d",&num);
           heap=malloc(num*sizeof(struct heaparr));
           for(i=0;i<num;i++)
             scanf("%d %f %s",&heap[i].key,&heap[i].data,heap[i].name);//access members and store data
          for(i=0;i<num;i++)
             printf("%d %f %s",heap[i].key,heap[i].data,heap[i].name);//print the data
      
      return 0;
      }
      

      【讨论】:

      • malloc的返回不用强制转换,没必要。请参阅:Do I cast the result of malloc?scanf (... &amp;heap[i].name); 应该是 heap[i].namename 已经是一个指针。)如果您不验证 scanfreturn - 您正在邀请 Undefined Behavior。避免使用"Try ...." 回答。它使您看起来不确定答案。
      • 下次我会弥补我的错误......谢谢@DavidC.Rankin
      • 没什么大不了的。我只是想提供帮助,并帮助确保答案尽可能地提供信息。我没有投反对票,因为你还是比较新的。永远记住,在回答时,您会步入老师的名册。你想成为一个好人,而不是因为其他原因我们都能记住的人:)
      • 不错的答案,但可以改进。清晰的答案必须带有清晰的代码。缩进和间距是实现这一目标的良好编程实践。丑陋的代码即使有用也可能会被否决,或者不太有用的代码可能会因为更漂亮而被投赞成票甚至被接受为正确答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-12-14
      • 2015-08-09
      • 2014-12-09
      • 2021-04-02
      • 1970-01-01
      • 1970-01-01
      • 2018-10-26
      相关资源
      最近更新 更多