如何管理传递的许多小对象的分配?

时间:2013-09-24 07:39:17

标签: c memory-management

我正在编写一个程序来标记文本并根据标记化对其进行转换。标记由结构表示:

struct token {
    enum token_type type;
    size_t length;        /* as returned by strlen(token.text); */
    char text[];          /* 0-terminated */
};

tokenizer提供了一个迭代器接口,在调用时分配并生成下一个标记。然后,此函数的调用者处理令牌,将其传递给多个函数(其中一些函数可能会自己存储令牌),并可能将其存储在某处。

在某个时间点,令牌处理已完成,所有令牌都可以被释放。

我应该如何继续追踪令牌的分配?

我有三个想法:

  • 每个标记都包含指向前一个标记的指针;最后,我可以简单地遍历链表以释放所有令牌。一旦我在多个地方创建令牌,这就变得复杂了。

  • 每个令牌都包含一个引用计数器。这很复杂,因为我需要密切注意我保留对令牌的引用。

  • 每个函数都复制令牌,而不是保留对它们的引用。如果函数传递一个标记作为参数,它就不能保留它们。这可能会导致大量不必要的内存分配。

从更有经验的程序员那里获得一些信息会很不错。

3 个答案:

答案 0 :(得分:2)

由于您说在某个时间点需要同时释放所有令牌,因此您可以使用内存池。编写一个令牌分配器,它将malloc令牌并将分配的指针存储在某个数组链表中,或者您想要解决它。完成所有处理后,调用一个可以同时释放所有令牌的函数。

像这样的东西(未经过测试,但你应该明白这一点):

struct token {
    struct token *token_next;
    enum token_type type;
    size_t length;
    char *text;
};

struct token_pool {
    struct token *token_list;
};

struct token *
token_alloc(struct token_pool *pool, size_t len, enum token_type type)
{
    struct token *t;

    if ((t = malloc(sizeof(*t) + len)) == NULL)
        return NULL;
    t->length = len;
    t->text = (char *)(t + 1);
    t->type = type;
    t->token_next = pool->token_list;
    pool->token_list = t;

    return t;
}

void
token_free_pool(struct token_pool *pool)
{
     struct token *t;

     while ((t = pool->token_list) != NULL) {
         pool->token_list = t->token_next;
         free(t);
     }
}

这里的高级水平是将内存分配给更大的平板,这样你就不需要多次调用malloc了,但是因为你要使用动态大小,所以它可能只是矫枉过正。

apache使用它们的apr_pool API。在apache中分配的许多内容都有非常特定的生命周期(每个服务器或每个调用),因此避免泄漏并优化分配和释放非常容易和有效。

答案 1 :(得分:1)

这取决于令牌的寿命以及你有多少额外的记忆。正在做与令牌明显不同的事情(例如创建一个新字符串,或者将其存储以供日后使用)应该可以创建一个副本。

对于其余的我会使用引用计数器 - 但是创建一个函数(或宏)来创建引用并更新计数器(以及释放引用并递减计数器的对应物)。

然后更容易找到错误的引用,因为任何时候你必须使用该函数 - 如果你不这样做它是不安全的。您甚至可以在其中放置调试逻辑,以检查何时释放引用,并且如果引用是安全的,则可以通过某种方式跟踪引用。在调试模式下,您可以将参考生成实现为副本,以允许您添加跟踪信息。

答案 2 :(得分:0)

也许简单的主列表就足够了。

分配令牌时,将令牌指针添加到主列表。完成处理后,请浏览列表并取消分配所有内容。

修改

这可以通过拥有一个Tokenizer对象(一个结构)来实现,该对象具有一系列功能,可以自动管理令牌的生命周期。

typedef struct {
    /* list for tokens */
} Tokenizer;

void Tokenizer_Init(Tokenizer * this); // Must be called first
void Tokenizer_DeInit(Tokenizer * this); // Must be called last
void Tokenizer_DoStuff(Tokenizer * this);