在c ++和vector / lists之前的几天,当他们需要存储更多数据时,他们是如何扩展数组的大小的呢?
答案 0 :(得分:34)
典型的C代码如下所示:
void* newMem = realloc(oldMem, newSize);
if(!newMem)
{
// handle error
}
oldMem = newMem;
请注意,如果realloc失败,则返回零但旧内存仍然有效,这种典型用法会导致内存泄漏:
oldMem = realloc(oldMem, newSize);
if(!oldMem)
{
// handle error
}
不幸的是,这很常见;
另请注意,C ++ vector / list没有什么特别之处。类似的结构可以用C实现,只是语法(和错误处理)看起来不同。例如,请参阅LodePNG's类似std::vector for C。
答案 1 :(得分:26)
许多C项目最终实现了类似矢量的API。动态数组是如此常见的需求,尽可能抽象出内存管理是很好的。典型的C实现可能类似于:
typedef struct dynamic_array_struct
{
int* data;
size_t capacity; /* total capacity */
size_t size; /* number of elements in vector */
} vector;
然后他们会有各种API函数调用,它们在vector
:
int vector_init(vector* v, size_t init_capacity)
{
v->data = malloc(init_capacity * sizeof(int));
if (!v->data) return -1;
v->size = 0;
v->capacity = init_capacity;
return 0; /* success */
}
当然,您需要push_back
,insert
,resize
等功能,如果realloc
超过size
,则会调用capacity
}。
vector_resize(vector* v, size_t new_size);
vector_push_back(vector* v, int element);
通常,当需要重新分配时,capacity
会加倍,以避免一直重新分配。这通常与std::vector
内部采用的策略相同,但由于C ++对象构造/销毁,std::vector
通常不会调用realloc
。相反,std::vector
可能会分配一个新缓冲区,然后将构造/移动构建对象(使用placement new
)构建到新缓冲区中。
C中的实际向量实现可能使用void*
指针作为元素而不是int
,因此代码更通用。无论如何,这种事情在许多C项目中实现。有关C中的示例向量实现,请参阅http://codingrecipes.com/implementation-of-a-vector-data-structure-in-c。
答案 2 :(得分:9)
他们将首先隐藏定义一个结构,该结构将保存实现所需的成员。然后提供一组操作结构内容的函数。
这样的事情:
typedef struct vec
{
unsigned char* _mem;
unsigned long _elems;
unsigned long _elemsize;
unsigned long _capelems;
unsigned long _reserve;
};
vec* vec_new(unsigned long elemsize)
{
vec* pvec = (vec*)malloc(sizeof(vec));
pvec->_reserve = 10;
pvec->_capelems = pvec->_reserve;
pvec->_elemsize = elemsize;
pvec->_elems = 0;
pvec->_mem = (unsigned char*)malloc(pvec->_capelems * pvec->_elemsize);
return pvec;
}
void vec_delete(vec* pvec)
{
free(pvec->_mem);
free(pvec);
}
void vec_grow(vec* pvec)
{
unsigned char* mem = (unsigned char*)malloc((pvec->_capelems + pvec->_reserve) * pvec->_elemsize);
memcpy(mem, pvec->_mem, pvec->_elems * pvec->_elemsize);
free(pvec->_mem);
pvec->_mem = mem;
pvec->_capelems += pvec->_reserve;
}
void vec_push_back(vec* pvec, void* data, unsigned long elemsize)
{
assert(elemsize == pvec->_elemsize);
if (pvec->_elems == pvec->_capelems) {
vec_grow(pvec);
}
memcpy(pvec->_mem + (pvec->_elems * pvec->_elemsize), (unsigned char*)data, pvec->_elemsize);
pvec->_elems++;
}
unsigned long vec_length(vec* pvec)
{
return pvec->_elems;
}
void* vec_get(vec* pvec, unsigned long index)
{
assert(index < pvec->_elems);
return (void*)(pvec->_mem + (index * pvec->_elemsize));
}
void vec_copy_item(vec* pvec, void* dest, unsigned long index)
{
memcpy(dest, vec_get(pvec, index), pvec->_elemsize);
}
void playwithvec()
{
vec* pvec = vec_new(sizeof(int));
for (int val = 0; val < 1000; val += 10) {
vec_push_back(pvec, &val, sizeof(val));
}
for (unsigned long index = (int)vec_length(pvec) - 1; (int)index >= 0; index--) {
int val;
vec_copy_item(pvec, &val, index);
printf("vec(%d) = %d\n", index, val);
}
vec_delete(pvec);
}
除此之外,他们将通过在函数组的vec *位置使用void *来实现封装,并且实际上通过在包含函数组而不是标题的C模块中定义结构定义来隐藏用户的结构定义。 。他们还会隐藏你认为是私有的函数,将它们从标题中删除,只是在C模块中进行原型设计。
答案 3 :(得分:3)
您可以看到实施vc_vector:
ng-init
使用它:
struct vc_vector {
size_t count;
size_t element_size;
size_t reserved_size;
char* data;
vc_vector_deleter* deleter;
};
...
vc_vector* vc_vector_create_copy(const vc_vector* vector) {
vc_vector* new_vector = vc_vector_create(vector->reserved_size / vector->count,
vector->element_size,
vector->deleter);
if (unlikely(!new_vector)) {
return new_vector;
}
if (memcpy(vector->data,
new_vector->data,
new_vector->element_size * vector->count) == NULL) {
vc_vector_release(new_vector);
new_vector = NULL;
return new_vector;
}
new_vector->count = vector->count;
return new_vector;
}
答案 4 :(得分:0)
https://github.com/jakubgorny47/baku-code/tree/master/c_vector
这是我的实现。基本上,它是一个结构,其中包含指向数据的指针,大小(以元素为单位),总分配空间以及要存储在vector中以允许使用void指针的类型的大小。
答案 5 :(得分:0)
您可以使用“ Gena ”库。它与纯{strong> C89 中的stl::vector
非常相似。
自述文件具有以下功能:
vec[k][j]
; 您可以在此处查看: