也许没有办法以我喜欢的方式解决这个问题,但我不知道所有事情,所以我最好问......
我已经使用动态数组实现了一个简单的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
获取值并让库处理所有内存分配/释放?
答案 0 :(得分:2)
我想你想改变你对存储内容的想法。用户给你一个指向她分配的内存的指针,所以她应该期望解除分配它。你不需要memcpy或释放值,你只需要跟踪指针。推入队列应该将所有权转移到队列,并从队列中弹出应该将所有权转移回用户。所以你需要做的就是复制'val'指针。
此外,要在完成时清理队列存储,您可能需要queueDestroy(Queue* q)
功能。
编辑:
答案 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}将指针传递给调用者}。我改变主意,以为我完全恢复了,但是没有版本控制:)