是否有可能在自己面前有效地重新分配数据?

时间:2015-03-25 06:36:10

标签: c memory-management allocation realloc

我制作了这个示例代码来说明我的问题:

/**
 * begin        end
 *   v           v
 * XXXXXXXXXXXXXXXX
 * ^
 * data
 *   [===========] size
 * [==============] capacity
 */
typedef struct list_t
{
    int *data;
    int *begin;
    int *end;
    size_t size;
    size_t capacity;
} list_t;


/**
 * begin        end
 *   v           v
 * XXXXXXXXXXXXXXXX
 * ^
 * old_data
 *
 * becomes
 *
 * begin        end
 *   v           v
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 * ^
 * data
 */
void reserve_back(list_t *this) {
    int *old_data = this->data;

    this->capacity *= 2;
    this->data = (int *) realloc(this->data, this->capacity * sizeof(int));

    if (old_data != this->data) {
        this->begin = this->begin - old_data + this->data;
        this->end   = this->end - old_data + this->data;
    }
}

/**
 * begin        end
 *   v           v
 * XXXXXXXXXXXXXXXX
 * ^
 * old_data
 *
 * becomes
 *
 *                 begin        end
 *                   v           v
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 * ^
 * data
 */
void reserve_front(list_t *this) {
    int *old_data = this->data;

    this->data = (int *) malloc(this->data, this->capacity * sizeof(int) * 2);
    memmove(this->data + this->capacity, old_data, this->capacity * sizeof(int));
    free(old_data);

    this->capacity *= 2;
    this->begin = this->begin - old_data + this->data;
    this->end   = this->end - old_data + this->data;
}

list_t基本上是一个双端动态数组,它在两端以恒定时间提供推送和弹出操作,以及通常的随机访问。

当我需要增加后面的容量时,我可以使用realloc重新分配数据块,而realloc只在必要时移动数据。 但是,为了增加前端的容量,我不得不每次都移动数据,这在大型列表上会变得非常沉重。

当已经分配的数据之前有可用内存时,有没有办法在常量时间内进行这种重新分配?

1 个答案:

答案 0 :(得分:2)

总之,没有。 (除非你编写自己的内存分配器,如果你尝试了,你很快就会明白为什么它没有在公共库分配器中实现。)

一般来说,实现双端队列(deque)的最佳方法是将数据保持在段中,而不是试图保留单个连续的向量。只要这些段具有合理的大小,这几乎与用于索引访问或迭代的单个连续缓冲区一样快,并且更快地将数据添加到任一端。

分段表示的一个缺点是您无法将deque的内容传递给需要简单向量的函数。另一方面,如果你不经常这样做,你可能会因为观察到制作deque的扁平副本并不比复制矢量更昂贵以便将它扩展到更大记忆分配。