带有void指针的动态数组队列数据结构问题

时间:2010-04-18 01:55:42

标签: c data-structures queue void-pointers dynamic-arrays

也许没有办法以我喜欢的方式解决这个问题,但我不知道所有事情,所以我最好问......

我已经使用动态数组实现了一个简单的Queue,因此用户可以使用它想要的任意数量的项目进行初始化。我也试图使用void指针来允许任何数据类型,但这就是问题所在。

这是我的代码:

typedef void * QueueValue;

typedef struct sQueueItem {
    QueueValue value;
} QueueItem;

typedef struct sQueue {
    QueueItem *items;

    int first;
    int last;
    int size;
    int count;
} Queue;

void queueInitialize(Queue **queue, size_t size) {
    *queue = xmalloc(sizeof(Queue));

    QueueItem *items = xmalloc(sizeof(QueueItem) * size);

    (*queue)->items = items;
    (*queue)->first = 0;
    (*queue)->last = 0;
    (*queue)->size = size;
    (*queue)->count = 0;
}

Bool queuePush(Queue * const queue, QueueValue value, size_t val_sz) {
    if(isNull(queue) || isFull(queue)) return FALSE;

    queue->items[queue->last].value = xmalloc(val_sz);
    memcpy(queue->items[queue->last].value, value, val_sz);

    queue->last = (queue->last+1) % queue->size;
    queue->count += 1;

    return TRUE;
}

Bool queuePop(Queue * const queue, QueueValue *value) {
    if(isEmpty(queue)) return FALSE;

    *value = queue->items[queue->first].value;

    free(queue->items[queue->first].value);

    queue->first = (queue->first+1) % queue->size;
    queue->count -= 1;

    return TRUE;
}

问题在于queuePop功能。当我打电话给它时,我失去了价值,因为我马上把它释放了。我似乎无法解决这个困境。我希望我的库是通用的和模块化的。用户不应该关心分配和释放内存,这是图书馆的工作。

用户如何仍然从queuePop获取值并让库处理所有内存分配/释放?

3 个答案:

答案 0 :(得分:2)

我想你想改变你对存储内容的想法。用户给你一个指向她分配的内存的指针,所以她应该期望解除分配它。你不需要memcpy或释放值,你只需要跟踪指针。推入队列应该将所有权转移到队列,并从队列中弹出应该将所有权转移回用户。所以你需要做的就是复制'val'指针。

此外,要在完成时清理队列存储,您可能需要queueDestroy(Queue* q)功能。

编辑:

  • 请注意,您不需要QueueItem,您可以存储QueueValues数组。
  • 另外,我意识到你明确表示内存管理是图书馆的工作,但那就是当你遇到问题时(比如你遇到的问题)。库应该处理自己的内存管理(队列)。但是分配& dealloc项目应该是用户的工作。
  • 另一种选择是提供一个queueFront(Queue * q),它传回该值,然后由queuePop(Queue * q)取消分配。我更喜欢你目前的做法:)
  • 要求用户定义队列的最大大小有点限制。如果你想要这是通用++,模块化++那么你应该使用预定义的常量,然后在queuePush()上增长,如果它已满。或者,您可以使用链接列表实现(但连续内存通常要快得多)。

答案 1 :(得分:2)

您的queuePop()功能需要与queuePush()的工作方式相同 - 获取位置的大小,并memcpy()

Bool queuePop(Queue * const queue, QueueValue value, size_t val_sz)
{
    if (isEmpty(queue)) return FALSE;

    memcpy(value, queue->items[queue->first].value, val_sz);

    free(queue->items[queue->first].value);

    queue->first = (queue->first+1) % queue->size;
    queue->count -= 1;

    return TRUE;
}

答案 2 :(得分:1)

其他人(正确地)指出了您的设计的严重限制,但这将解决您的问题。它假定调用者知道对象被推送和弹出的大小。

理论上,这些变化中只有两个是绝对必要的,但其他变更可以将崩溃的可能性(由于程序员错误)从~100%降低到~80%。

typedef struct sQueueItem {
    QueueValue value;
    size_t     item_size;               // <-- you'll need this for the Pop
} QueueItem;

Bool queuePush(Queue * const queue, QueueValue value, size_t val_sz) {
    if(isNull(queue) || isFull(queue)) return FALSE;

    queue->items[queue->last].value = xmalloc(val_sz);
    memcpy(queue->items[queue->last].value, value, val_sz);
    queue->items[queue->last].item_size = val_sz;        // <-- save the size

    queue->last = (queue->last+1) % queue->size;
    queue->count += 1;

    return TRUE;
}

Bool queuePop(Queue * const queue, 
               QueueValue **value, // ESSENTIAL: now char **
               size_t item_size)   // so we can ensure enough room
{                                         
    if(isEmpty(queue)) return FALSE;

     // just for readability
    QueueItem *p = queue->items[queue->first];

    // watch for programmer error (maybe you should throw() something)
    assert(p->item_size == item_size);       

    // ESSENTIAL: copy the item to the caller's memory
    memcpy(*value, p->value, p->item_size); 

    free(queue->items[queue->first].value);

    queue->first = (queue->first+1) % queue->size;
    queue->count -= 1;

    return TRUE;
}

修改

有人指出我可以将queuePop作为

Bool queuePop(Queue * const queue, 
               QueueValue *value,  // stet
               size_t item_size)   // so we can ensure enough room

and changed the `memcpy` to 

    // ESSENTIAL: copy the item to the caller's memory
    memcpy(value, p->value, p->item_size); 

我曾经写过它,所以如果调用者在item_size中传递NULL,queuePop将执行malloc()并通过{{1}将指针传递给调用者}。我改变主意,以为我完全恢复了,但是没有版本控制:)