【问题标题】:How can i automatically increase memory allocation for a struct in C?如何自动增加 C 中结构的内存分配?
【发布时间】:2019-04-05 20:02:02
【问题描述】:

我正在构建一个小程序,它将姓名和年龄作为输入(存储在结构中)并输出输出。我面临的问题之一是我必须输入要存储的人数,我确信我可以用realloc() 解决这个问题,但它只是不起作用。这是我目前得到的。

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

struct info
{
    int age;
    char name[30];
};

int main()
{
    struct info *Ptr;
    int i, num;

    printf("Enter number of people");
    scanf("%d", &num);

    // Allocates the memory for num structures with pointer Ptr pointing to the base address.
    Ptr = (struct info*)malloc(num * sizeof(struct info));

    for(i = 0; i < num; ++i)
    {
        printf("Enter name and age:\n");
        scanf("%s %d", &(Ptr+i)->name, &(Ptr+i)->age);
    }


    for(i = 0; i < num ; ++i)
        printf("Name = %s, Age = %d\n", (Ptr+i)->name, (Ptr+i)->age);

    return 0;
}

我试图在第一个 for 循环中重新分配,但它没有工作,即使它在那里是有意义的。还尝试将循环转换为 while 循环,如下所示:

     while(input != "stop)
    {
      allocate more memory
}

如何使用 realloc 来避免在输入之前输入人员编号?

【问题讨论】:

  • 我知道,但我不能这样做,然后才能以正确的方式执行 realoc,然后才能继续比较结构的输入和输入变量
  • 你能清楚地解释你想要达到的目标吗?你不想拿人数,然后如果人数增加就调整,这就是你想要达到的目标吗?
  • 它不起作用”实际上什么不起作用?
  • 虽然 realloc 允许您扩展分配,但它可能非常低效。如果记录不需要位于连续内存中,则链表可能是合适的。添加记录快速高效,访问记录较少。

标签: c memory-management


【解决方案1】:

realloc 是正确的方法。只需从Ptr = NULLnum = 0 开始,然后在每个输入上将元素数量加一。

记得限制 scanf 可以读取的字符数,否则可能会导致缓冲区溢出。

我还发现Ptr[i](Ptr+i)-&gt; 更容易。

还将字符串与strcmp 进行比较,而不是使用!=!= 将比较指向字符串的指针,而不是字符串本身。

因为我喜欢阅读整行,然后扫描该行,所以我会这样做:

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

struct info
{
    int age;
    char name[30];
};

int main()
{
    struct info *ptr = 0;
    size_t num = 0;

    for (;;) {
        printf("Enter name and age. If you want to stop, type only 'stop'.\n");

        char line[256];
        if (fgets(line, sizeof(line), stdin) == NULL) { 
             fprintf(stderr, "fgets error");
             exit(-1);
        }

        if (!strcmp("stop\n", line)) {
             break;
        }

        struct info tmp;
        if (sscanf(line, "%29s %d\n", tmp.name, &tmp.age) != 2) {
             fprintf(stderr, "error parsing line\n");
             exit(-1);
        }

        ptr = realloc(ptr, (num + 1) * sizeof(*ptr));
        if (ptr == NULL) { 
             fprintf(stderr, "error allocating memory!\n");
             exit(-1);
        }

        ptr[num] = tmp;
        ++num;
    }


    for (size_t i = 0; i < num ; ++i) {
        printf("Name = %s, Age = %d\n", ptr[i].name, ptr[i].age);
    }

    free(ptr);

    return 0;
}

【讨论】:

  • if (fgets(line, sizeof(line), stdin) &lt; 0) {...} fgets() 返回一个 char* 。这可能是 NULL,但绝不是 &lt; 0 你的编译器应该警告过你。
  • @joop 对,对,你是对的。那个标准库,每个函数都有不同的错误处理。
  • 如果指针指向同一个数组(或者一个是数组,另一个指向最后一个元素之后的一个),则可以进行指针运算。但我不认为比较两个指针是否相等是不确定的。
  • 感谢您的回复,但我注意到您没有使用 malloc,这怎么可能?
  • @HannaM:“不使用 malloc,这怎么可能?”您可能想阅读这个答案:stackoverflow.com/a/12134328/694576
【解决方案2】:

如果您不确定要分配的元素数量并根据用户的选择进行分配,那么您可以按照以下方法。

它从一个元素开始,当用户想要添加新元素时重新分配内存。

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

struct info
{
  int age;
  char name[30];
};

int main()
{
    struct info *Ptr=NULL;
    int i=0, num;

    char c='Y';
    while(c=='Y'||c=='y') {

            Ptr=realloc(Ptr,(i+1)*sizeof(struct info));
            if(Ptr==NULL)
                 break;
            printf("Enter name and age:\n");
            scanf("%s %d",&Ptr[i].name,&Ptr[i].age);
            printf("Do you want to cont?\n");
            scanf(" %c",&c);
            i++;
    }
    num=i;

    for(i = 0; i < num ; ++i)
            printf("Name = %s, Age = %d\n", (Ptr+i)->name, (Ptr+i)->age);
    free(Ptr);
    return 0;
}

【讨论】:

    【解决方案3】:

    要准确回答,您可以先读取临时变量的输入并检查是否需要停止:如果需要,则中断循环。或者继续并重新分配 'storage' 数组,方法是将其大小增加 1 并将刚刚读取的值复制到 'storage' 数组中。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    struct          info
    {
      int           age;
      char          name[30];
    };
    
    int             main()
    {
      struct info * infos = 0;
      int           num = 0;
      char          input_name[30];
      int           input_age;
    
      while (1)
        {
          printf("Enter name and age:\n");
          int r = scanf("%29s", input_name);
          if (r == EOF || strcmp(input_name, "stop") == 0)
            break;
          scanf(" %d", &input_age);
    
          infos = realloc(infos, sizeof(struct info) * (num + 1));
          infos[num].age = input_age;
          memcpy(infos[num].name, input_name, sizeof(char) * 30);
          num++;
        }
    
      for(int i = 0; i < num ; ++i)
        printf("Name = %s, Age = %d\n", infos[i].name, infos[i].age);
    
      return 0;
    }
    

    【讨论】:

    • 这个"%s"应该是"%29s"以避免溢出input_name
    • 也缺少对scanf() 的第二次调用以及realloc() 的错误检查。
    • 而这个测试 (strcmp(input_name, "stop") == 0 || r == EOF) 必须反过来:(r == EOF || strcmp(input_name, "stop")) 因为你不想在 EOF 的情况下调用 strcmp()
    【解决方案4】:

    您应该使用像vector 这样的数据结构。

    1. vector_init 初始化向量。
    2. vector_pushvalvector,如果需要,会realloc内存。
    3. vector_output 输出vector

    以下code 可以工作:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define INIT_SIZE 1
    
    struct info
    {
        int age;
        char name[30];
    };
    
    struct vector {
        struct info* p;
        int n;
        int index;
    };
    
    void vector_init(struct vector* ve) {
        ve->n     = INIT_SIZE;
        ve->index = 0;
        ve->p     = malloc(sizeof(struct info) * ve->n);
    }
    
    void vector_push(struct vector* ve, struct info* tmp) {
        if (ve->n == ve->index) {
            ve->n *= 2;
            ve->p = realloc(ve->p, sizeof(struct info) * ve->n);
        }
        ve->p[ve->index++] = *tmp;
    }
    
    void vector_output(const struct vector* ve) {
        for (int i = 0; i < ve->index; ++i)
            printf("Name = %s, Age = %d\n", ve->p[i].name, ve->p[i].age);
    }
    
    int main()
    {
        struct vector ve;
        vector_init(&ve);
    
        for (;;) {
            struct info tmp;
            printf("Enter name and age:\n");
            scanf("%29s", tmp.name);
            if (strcmp(tmp.name, "stop") == 0)
                break;
            scanf("%d", &tmp.age);
            vector_push(&ve, &tmp);
        }
        vector_output(&ve);
    
        return 0;
    }
    

    【讨论】:

    • 这在长程序中工作和简化,我认为添加 cmets 和解释会更好。
    • 这个“%s”应该是“%29s”以避免溢出。
    • 完全没有错误检查。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-02
    • 2011-11-13
    • 2021-10-15
    相关资源
    最近更新 更多