【问题标题】:Freeing a dynamically allocated 2d array only freeing first two rows释放动态分配的二维数组仅释放前两行
【发布时间】:2022-01-15 01:38:12
【问题描述】:

所以我有一个程序,我用以下函数创建一个动态二维数组。

char **get2dArray(int n, int m)
{
  {
    char **p = (char **)calloc(sizeof(char *), n);

    for (int i = 0; i < n; i++)
    {
      p[i] = (char *)calloc(m, sizeof(p[0]));
    }

    return p;
  }
}

函数在创建后填充了一些数据。它基本上在每个 2d_arr[i] 中包含一个句子。

void getSentencesWithNumber(int sentence_total, char **sentences_seperated_2d)
{
  printf("Ihre Wahl:  Nr. | Satz\n-----+----------------------------------------\n");
  for (int i = 0; i < sentence_total; i++)
  {
    printf("   %d | %s\n", i + 1, sentences_seperated_2d[i]);
  }
}

我用上面的函数打印句子。

Ihre Wahl:  Nr. | Satz
-----+----------------------------------------
   1 | A paragraph is a series of related sentences developing a central idea, called the topic.
   2 | Try to think about paragraphs in terms of thematic unity: a paragraph is a sentence or a group of sentences that supports one central, unified idea.
   3 | Paragraphs add one idea at a time to your broader argument.

以上是打印外观的示例。 我想从数组中删除一个句子,但是我不确定它是如何工作的,所以我认为也许释放当前的二维数组并用更少的句子制作一个新的数组会起作用。但是,如果我使用免费并再次打印我的数组,它看起来有点像这样:

Ihre Wahl:  Nr. | Satz
-----+----------------------------------------
   1 | (null)
   2 | 
   3 | Paragraphs add one idea at a time to your broader argument.

在上面,数组在填充后立即被释放。并且打印函数是从另一个函数调用的。如果我输出是一样的

for (int i = 0; i < sentence_total; i++)
{
free(sentences_seperated_2d[i]);
}
free(sentences_seperated_2d);

以上是我用的免费的。

您知道这里只删除前 2 个句子而不删除其他句子吗?对我来说这毫无意义。此外,即使有更多的句子,也只有前两个被清空。

谢谢,我希望我把我的问题说清楚了。

【问题讨论】:

  • free 不会“清空”内容。它只是将内存返回给分配器。永远不要访问释放的内存。这样做会导致未定义的行为,您不能对结果有任何期望。
  • p[i] = (char *)calloc(m, sizeof(p[0])); 错误,这给出了m * sizeof (char*)。您应该使用 p[i] = calloc(m, 1); 或等效的。不确定这是否是问题的根本原因,但这是一个错误。
  • free() 在技术上是声明指向的对象永远不会再被访问。所以编译器/运行时环境可以做任何它认为适合对象内存的事情。任何违反本声明的行为都会导致 UB。
  • 您没有说明您所期望的行为,所以我们不知道您有什么错误的想法。在调用free 之后,您是否期望内存包含零? free 不需要并且通常不会故意将内存归零。 (可能有可以打开的调试功能来执行此操作,free 可能会更改一些内存以将其用于自己的数据库目的。)您是否期望在调用free 后内存无法访问? free 不需要并且通常不会取消映射虚拟内存(尽管它可能用于大分配)......
  • 您是否希望在调用free 之后将指针设置为空? free 无法更改用于向其传递地址的实际对象。 (但是,编译器优化可能会导致对象被更改的效果,但是,在这方面,C 语言的形式语义使指针无效,而不是 null。)

标签: c dynamic-memory-allocation free dynamically-generated


【解决方案1】:

我不明白你是否必须使用二维数组,或者是你选择的解决方案。由于您打算删除打印的内容,因此该数组似乎不是您需要的正确数据类型。我建议你使用双链表。创建一个递归结构,如:

struct INFO {
    int id;
    char *description;
    struct INFO *parent_node;
    struct INFO *next_node;
}
struct INFO *root = malloc(sizeof(struct INFO));

所以当你想删除条目时,只需将当前的 next_node 复制到前一个节点的那个(包含当前节点的地址),然后释放当前节点。 只是一个如何提供列表的示例:

for(;;)
{
    id  = get_id();
    des = get_des();
    if(des == NULL)
        break;
    last->next_node = malloc(sizeof(struct INFO));
    last->next_node->parent_node = last;
    last = last->next_node;
    last->id = id;
    last->description = des;
    last->next_node = NULL;
}

为了简化您的测试,我添加了我使用的 2 个函数。一切都写得不准确,你必须测试分配的结果(即使我不明白为什么如果你不在 C 中测试 malloc 的结果你犯了罪,而在 C++ 中我看到'新'到处都是未经测试的并且没有人抱怨)。

int get_id()
{
    int id;

    printf("\nid: ");
    scanf("%d", &id);
    getchar();
    return id;
 }

char *get_des()
{
    int i, c, s_ch = sizeof(char);
    char *des = malloc(1 * s_ch);
    printf("\ndescription: ");
    for(i = 0; (c = getchar()) != '\n'; i++)
    {
        des[i] = c;
        des = realloc(des, 1 * s_ch);
    }
    des[i] = '\0';
    if(!strcmp(des, ""))
    {
        free(des);
        des = NULL;
    }
    return des;
}

打印列表:

for(p = root; p != NULL; p = p->next_node) /* show list */
{
    printf("\nid: %d description: %s", p->id, p->description);
}

搜索和删除:

for(p = root; p != NULL && p->id != 2; p = p->next_node); /* find */

if(p != NULL) /* show and delete */
{
    printf("\n\nfound: id: %d description: %s", p->id, p->description);
    if(p->parent_node != NULL)
        p->parent_node->next_node = p->next_node;
    else
        root = p->next_node;
    free(p);
}

【讨论】:

  • 我们还没有开始使用结构,所以我对它们不是很熟悉,但是我通过将指针切换到我的二维数组并释放旧数组来实现我想做的事情。它工作得很好,但是这是一种不好的方法还是只是一种不熟悉的方法,因为分配一个新数组并释放它需要时间?
  • 如果您认为分配时间太长,您可以简单地将要删除的内容移到数组底部并向上翻译其他字段,然后删除字段
  • 哦,这也有道理,谢谢
猜你喜欢
  • 1970-01-01
  • 2018-09-01
  • 2012-01-08
  • 2016-03-15
  • 1970-01-01
  • 1970-01-01
  • 2019-07-08
  • 2018-11-10
  • 2019-11-30
相关资源
最近更新 更多