如何在c中复制向量?

Kai*_*ije 39 c

在c ++和vector/lists之前的几天,当他们需要存储更多数据时,他们是如何扩展数组的?

ybu*_*ill 35

典型的C代码如下所示:

void* newMem = realloc(oldMem, newSize);
if(!newMem)
{
    // handle error
}

oldMem = newMem;
Run Code Online (Sandbox Code Playgroud)

请注意,如果realloc失败,则它返回零但旧内存仍然有效,这种典型用法会导致内存泄漏:

oldMem = realloc(oldMem, newSize);
if(!oldMem)
{
    // handle error
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,它很常见;

另请注意,C++ vector/list没有什么特别之处.类似的结构可以用C实现,只是语法(和错误处理)看起来不同.例如,参见LodePNG对C的std :: vector 模拟.

  • @ybungalobill,嗯...`vector :: clear()`与"free"无关. (3认同)
  • 这些都不正确。new 调用对象构造以及内存分配,delete 调用对象删除以及将内存返回给系统。realloc 可用于分配、释放和复制内存,但它不能构造 C++ 意义上的对象。另请注意,malloc、free 和 realloc 的行为与 new 和 delete 不同。malloc 可以在分配失败时返回零,而 new 将引发异常。另外,在空指针上使用 free 会导致取消引用空指针,而在空指针上使用 delete 将不会执行任何操作 (2认同)
  • @Chris 我说过的 C89 标准,“4.10.3.2 free 函数导致 ptr 指向的空间被释放,也就是说,可用于进一步分配。_如果 ptr 是空指针,则不会发生任何操作。_” (2认同)

Cha*_*via 28

许多C项目最终实现了类似矢量的API.动态数组是如此常见的需求,尽可能抽象出内存管理是很好的.典型的C实现可能类似于:

typedef struct dynamic_array_struct
{
  int* data;
  size_t capacity; /* total capacity */
  size_t size; /* number of elements in vector */
} vector;
Run Code Online (Sandbox Code Playgroud)

然后他们会有各种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 */
}
Run Code Online (Sandbox Code Playgroud)

然后,当然,你需要的功能push_back,insert,resize,等,这将调用realloc,如果size超出capacity.

vector_resize(vector* v, size_t new_size);

vector_push_back(vector* v, int element);
Run Code Online (Sandbox Code Playgroud)

通常,当需要重新分配时,capacity会加倍,以避免一直重新分配.这通常与内部使用的策略相同std::vector,除非通常std::vector不会realloc因C++对象构造/销毁而调用.相反,std::vector可能会分配一个新缓冲区,然后将构造/移动构造对象(使用放置new)复制到新缓冲区中.

C中的实际向量实现可能使用void*指针作为元素而不是int,因此代码更通用.无论如何,这种事情在许多C项目中实现.有关C中的示例向量实现,请参阅http://codingrecipes.com/implementation-of-a-vector-data-structure-in-c.

  • @WarlikeChimpanzee 你的链接现在也坏了。 (2认同)

Mic*_*ith 10

他们将首先隐藏定义一个结构,该结构将保存实现所需的成员.然后提供一组操作结构内容的函数.

像这样的东西:

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);
}
Run Code Online (Sandbox Code Playgroud)

除此之外,他们将通过在函数组的vec*位置使用void*来实现封装,并且实际上通过在包含函数组而不是标题的C模块中定义结构定义来隐藏用户的结构定义.此外,他们会隐藏您认为是私有的功能,将它们从标题中删除,只是在C模块中对它们进行原型设计.


far*_*ost 5

你可以看到实现vc_vector

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;
}
Run Code Online (Sandbox Code Playgroud)

要使用它:

vc_vector* v1 = vc_vector_create(0, sizeof(int), NULL);
for (int i = 0; i < 10; ++i) {
  vc_vector_push_back(v1, &i);
}

// v1 = 0 1 2 3 4 5 6 7 8 9

vc_vector* v2 = vc_vector_create_copy(v1);

// v2 = 0 1 2 3 4 5 6 7 8 9 (copy of v1)

// to get pointer to int:

const int* v2_data = vc_vector_data(v1);
Run Code Online (Sandbox Code Playgroud)