【问题标题】:C dynamically allocate struct array and its componentsC动态分配结构数组及其组件
【发布时间】:2019-04-28 23:12:57
【问题描述】:

在 C 中动态分配结构数组及其组件的正确方法是什么?我已经设法做了一些有效的事情,但我有点怀疑它是否正确。 我有以下代码:
这是我需要动态分配的结构数组:

typedef struct
{
    char *wrong;
    char *right;
}Dictionary;

这是我需要初始化结构数组时调用的函数:

Dictionary *init_Dictionary(int nr_elem)
{
    Dictionary *dict;
    dict = malloc(nr_elem*sizeof(Dictionary));
    for(int i=0; i<nr_elem; i++)
    {
        char wrong[101],right[101];
        scanf("%s%s",wrong,right);
        dict[i].wrong = malloc(strlen(wrong)*sizeof(char));
        dict[i].right = malloc(strlen(right)*sizeof(char));
        strcpy(dict[i].wrong,wrong);
        strcpy(dict[i].right,right);
    }
    return dict;
}

然后在我的主要功能中,我有这个:

    int nr_elem;
    scanf("%d",&nr_elem);
    Dictionary *dict;
    dict = init_Dictionary(nr_elem);

另外,当我完成结构的工作后,如何释放使用的内存?
编辑感谢大家快速而深入的回答!

【问题讨论】:

  • dict[i].wrong = malloc(strlen(wrong)*sizeof(char)); -> dict[i].wrong = malloc(strlen(wrong) + 1);(你需要一个额外的字符作为字符串终止符,sizeof(char) 是多余的)。
  • 不要忘记 C 中的 char 字符串实际上称为 null-terminated 字节字符串,并且 null-终结者也需要空间,并且strlen计算。
  • 你需要将每个malloc(或类似的)与free匹配。
  • @Someprogrammerdude 所以我必须遍历我的结构数组并在结构的每个组件上免费调用?
  • 是的,在循环 free 之后也是结构的“数组”。

标签: c arrays struct dynamic-memory-allocation


【解决方案1】:

程序设计不好,你应该把 UI 和算法分开。取而代之的是,您应该首先获取用户输入,然后将其存储在 2 个字符串中,并将字符串作为参数传递给 init_Dictionary

至于分配,几乎是正确的。但是你忘了给空终止符分配空间,应该是:

dict[i].wrong = malloc(strlen(wrong)+1);
dict[i].right = malloc(strlen(right)+1);

sizeof(char) 相乘没有意义,因为sizeof(char) 的定义在所有系统上总是1

在生产质量的应用程序中,您必须始终检查每个malloc 的结果,然后处理错误。

您释放内存的方式与分配内存的方式相同,但顺序相反,因为您需要 dict 本身在释放其成员之前有效:

for(int i=0; i<nr_elem; i++)
{
  free(dict[i].wrong);
  free(dict[i].right);
}
free(dict);

根据经验,对malloc 的每次调用都必须与对free 的调用相匹配。

【讨论】:

  • 我会在释放后将所有指针设置为空,以避免重复使用。
  • @TsakiroglouFotis 虽然这当然是一种很好的做法,但它的可行性取决于具体情况。如果你有 void cleanup (Dictionary* d){ free(d); } 之类的函数,那么将指针设置为 NULL 不会有好处,因为它不会返回给调用者。
  • 我明白你的意思。但是,当你与其他人一起工作时,最好避免将来有人破坏你的代码。例如,有人拿走了你的代码并添加了另一个 free()。
【解决方案2】:

您的实现中有一个错误:strlen(s) 不计算终止的 0 字符,因此,尽管一个测试可能成功,但这实际上是一个 UB。 strdup可以为你工作;如果您没有标准库,只需在为字符串副本分配内存时添加 1。甚至更好:计算一次字符串长度,然后使用该值分配足够的字节并使用memcpy复制内容。

否则,您的算法将非常有用(前提是您确实需要一个字符串对数组,而无需像搜索索引或任何其他结构)。

要释放它,添加一个析构函数来执行元素释放,然后释放整个数组:

void destroy(Dictionary *dict, size_t nr_elem) {
    for(size_t i = 0; i < nr_elem; ++i) {
        free(dict[i].wrong);
        free(dict[i].right);
    }
    free(dict);
}

【讨论】:

    【解决方案3】:

    对于每个分配,您需要再分配一个位置以允许在字符串末尾添加\0(NULL 终止符)。

    dict[i].wrong = malloc(strlen(wrong)*sizeof(char) +1 );
    dict[i].right = malloc(strlen(right)*sizeof(char) +1);
    

    要释放,首先需要释放数组中的所有指针rightwrong,然后释放主数组dict。或者,您可以在 free 之后将指针设为 NULL。

    Dictionary* freeDict(Dictionary *dict, int nr_elem)
    {
        for (int i=0; i<nr_elem; i++)
        {
            free(dict->wrong);
            free(dict->right);
            dict->wrong = NULL;
            dict->right = NULL;
        }
        free (dict);
        dict = NULL;
        return dict;
    }
    
    //To call.
    dict = free(dict, nr_elem);
    

    【讨论】:

    • dict-&gt;wrong = NULL; 这毫无意义,是吗?无论如何,您将要释放它。
    • 不,不是。在 free() 之后将指针设置为 null 以避免重用总是一个好习惯。
    • @TsakiroglouFotis 教条地遵循良好的实践规则而不应用常识是有害的。通过阅读这段代码,您可以看到它毫无意义。还是您希望代码的某些维护者在free(dict-&gt;wrong);free (dict); 之间使用dict-&gt;wrong 来发疯?这不会发生,所以它所做的只是减慢freeDict 函数的速度。
    • 我理解你所说的,我尊重这一点。我只是有另一个意见。dict的范围在这个函数之上。 dict 在其他地方创建,它可能在代码中。所以,让我们假设将来有人拿你的(比方说)5.000 行代码并为 dict 添加另一个免费的。
    猜你喜欢
    • 1970-01-01
    • 2014-08-22
    • 1970-01-01
    • 1970-01-01
    • 2021-10-30
    • 2017-03-30
    • 1970-01-01
    相关资源
    最近更新 更多