【问题标题】:Recursive struct and malloc()递归结构和 malloc()
【发布时间】:2011-11-27 18:17:02
【问题描述】:

我有一个递归的struct,它是:

typedef struct dict dict;
struct dict {
    dict *children[M];
    list *words[M];
};

以这种方式初始化:

dict *d = malloc(sizeof(dict));
bzero(d, sizeof(dict));

我想知道 bzero() 在这里到底做了什么,以及我如何为孩子递归地 malloc()

编辑:这就是我希望能够malloc() childrenwords 的方式:

void dict_insert(dict *d, char *signature, unsigned int current_letter, char *w) {
    int occur;
    occur = (int) signature[current_letter];
    if (current_letter == LAST_LETTER) {
        printf("word found : %s!\n",w);
        list_print(d->words[occur]);
        char *new;
        new = malloc(strlen(w) + 1);
        strcpy(new, w);
        list_append(d->words[occur],new);
        list_print(d->words[occur]);
    }
    else {
        d = d->children[occur];
        dict_insert(d,signature,current_letter+1,w);
    }
}

【问题讨论】:

  • 递归什么时候停止?
  • 根据手册页,bzero 已被弃用。请改用callocmalloc/memset
  • 我不明白在这里有bzero() 有什么意义?
  • @DietrichEpp 我有一个#define bzero(b,len) (memset((b), '\0', (len)), (void) 0) 定义来做到这一点。

标签: c struct malloc recursive-datastructures


【解决方案1】:

bzero(3) 将内存初始化为零。相当于调用memset(3),第二个参数为0。在这种情况下,它将所有成员变量初始化为空指针。 bzero 被认为已弃用,因此您应该用 memset 替换它的用法;或者,您可以只调用calloc(3) 而不是malloc,它会在成功时自动为您清零返回的内存。

您不应该使用您编写的两种转换中的任何一种——在 C 中,void* 指针可以隐式转换为任何其他指针类型,并且任何指针类型都可以隐式转换为 void*malloc 返回一个 void*,因此您可以将其分配给您的 dict *d 变量而无需强制转换。同样,bzero 的第一个参数是void*,因此您可以直接将d 变量传递给它,而无需强制转换。

要了解递归,您必须首先了解递归。如果您想避免无限分配内存,请确保您有适当的基本情况。

【讨论】:

  • 将内存初始化为零有什么意义?我已经删除了演员表。我希望能够只为我想要的孩子分配内存。我理解递归,但我不擅长分配内存。我可以在这里使用一些帮助:)
【解决方案2】:

一般来说,当您不确定编译器正在为您生成什么时,最好使用 printf 来报告结构的大小。在这种情况下, dict 的大小应该是 2 * M * 指针的大小。在这种情况下, bzero 将用零填充字典。换句话说,children 和 words 数组的所有 M 个元素都将为零。

要初始化结构,我建议创建一个函数,该函数接受一个指向 dict 的指针并对每个孩子进行 malloc,然后调用自身来初始化它:

void init_dict(dict* d)
{
    int i;
    for (i = 0; i < M; i++)
    {
        d->children[i] = malloc(sizeof(dict));
        init_dict(d->children[i]);

        /* initialize the words elements, too */
    }
}

如果您知道为什么此代码无法按原样工作,请向您 +1。 (提示:它有一个无限递归错误,需要一个规则来告诉它子树需要多深才能停止递归。)

【讨论】:

  • 需要一个depth 规则,就像你上面的答案中的那个:)
  • 正确!我让你知道它应该采取什么形式。我决定不使用像 Jerry's 这样的树,因为对我来说你需要一棵深度一致的树并不明显。也许分支的深度应该取决于存储在其中的数据。我说不出来。
  • 是的,深度是统一的:每个分支可以有 26 的深度(对于字母 a,深度 = 1,对于字母 z,深度 = 26)。我已经在 J​​erry 的回答中解释了我想要实现的目标,这可能会帮助您理解我的问题:)
  • 26?!?所以如果顶层节点有M个孩子,每个孩子有M个孩子,有26代孩子,那么就有M + M^2 + M^3 + ... + M^26个节点。那是很多节点。好在RAM很便宜。 :)
  • 是的,这就是为什么我不能为整个树分配内存,而只能为需要的节点分配内存。
【解决方案3】:

bzero 只是将内存归零。 bzero(addr, size) 本质上等同于 memset(addr, 0, size)。至于你为什么要使用它,从我所看到的大约一半的使用时间来看,这只是因为有人虽然将内存归零似乎是个好主意,尽管它并没有真正完成任何事情。在这种情况下,看起来效果是将一些指针设置为 NULL(尽管它不是完全可移植的)。

要递归分配,您基本上只需跟踪当前深度,然​​后分配子节点,直到达到所需的深度。按照这个顺序编写代码就可以了:

void alloc_tree(dict **root, size_t depth) { 
    int i;
    if (depth == 0) {
        (*root) = NULL;
        return;
    }

    (*root) = malloc(sizeof(**root));

    for (i=0; i<M; i++)
        alloc_tree((*root)->children+i, depth-1);       
}

我应该补充一点,虽然我无法想象像这样进行递归分配。在典型情况下,您插入数据,并根据需要分配新节点来保存数据。具体细节取决于您是否(以及如何)保持树平衡。对于像这样的多路树,使用一些 B-tree 变体是相当普遍的,在这种情况下,我上面给出的代码通常根本不适用——使用 B-tree,你填充一个节点,当它达到极限时,你将它分成两半并将中间项目提升到父节点。当它到达树的顶部时,您分配一个新节点,并且根节点已经满了。

【讨论】:

  • 是的,我想根据需要分配新节点。我编辑了我的帖子以添加用于插入数据的功能。我想知道如何测试是否已经为这个节点分配了内存,如果没有怎么分配。
  • @kh4r4:我认为在我(或可能任何人)可以为您提供更多帮助之前,我们需要更多地了解您正在尝试做的事情的整体结构。查看您添加的代码,我完全不确定我是否理解您真正想要完成的工作。
  • 我正在尝试完成以下操作:我们有一个单词列表,我们希望将它们插入到树中,以了解它们的字母出现的次数(一个字母可以在一个单词中出现 M 次) .首先我们检查当前的字母。如果我们到达最后一个字母,则将单词插入相应的列表中。如果不是,我们在当前节点的相应子节点上递归调用插入算法。
猜你喜欢
  • 2012-10-22
  • 2018-06-21
  • 2021-01-15
  • 2017-08-25
  • 2020-03-22
  • 2016-07-05
  • 2021-04-02
  • 2018-05-06
  • 1970-01-01
相关资源
最近更新 更多