如何获取向量的第i个元素

How to get the i-th element of vector

我想获取void*的第i个元素。我知道它是 void 类型,我必须给它一个特定的数据类型。这背后的想法是它应该适用于不同的数据类型。如果我对这个问题的看法是正确的,我该如何实施?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_STR_LEN 64

typedef struct Vector {
    void *data;
    size_t element_size;
    size_t size;
    size_t capacity;
} Vector;

// Allocate vector to initial capacity (block_size elements),
// Set element_size, size (to 0), capacity

void init_vector(Vector *vector, size_t block_size, size_t element_size){
    vector->data = (void*) malloc(block_size * element_size); // i am questioning whether this is correct
    vector->element_size = element_size;
    vector->size = 0;
    vector->capacity = block_size * element_size;
}

void resize(Vector *vector, size_t new_size){
    void *data2 = (void*) malloc(new_size * vector->element_size);
    int i=0;
    memmove(data2,vector->data,new_size * vector->element_size);
    vector->data = data2;
    if(new_size > vector->size){
        for(i=vector->size-1;i<new_size;i++){
            vector->data[i]=0;                           // here is the problem
        }
    }else{
        vector->size = new_size;
    }
    vector->capacity = new_size*vector->element_size;
}

您的矢量函数可以不能 [有意义地]使用索引或指针语法访问数组数据,例如:

vector->data[i]=0;

那是因为它是一个 void * 指针,但更重要的是,如果 element_size 是(例如)8,这是否意味着 vector->data 指向一个 doubleunsigned long long?

只有您函数的 调用者 可以执行此操作:

Vector *vec = calloc(1,sizeof(*vec));

init_vector(vec,100,sizeof(double));

double *ptr = vec->data;
for (size_t idx = 0;  idx < vec->size;  ++idx)
    ptr[idx] = idx;

当您扩展 resize 中的数组时,您只能 mem* 个函数。

将您的 for 循环替换为:

memset(&vector->data[vector->size * vector->element_size],0,
    (new_size - vector->size) * vector->element_size);

更新:

还有一些问题。尽管您 可以 capacity 成为 字节 计数,但更常见的是 元素 计数(就像 size)。

当我创建这样的动态 array/vector objects/functions 时,我通常 而不是 resize 对元素进行初始化。

那是因为它 [真的] 不知道如何初始化元素。对于 c++ 向量, 构造函数 知道。

所以,如果我们希望resize[和init_vector]这样做,我们需要为此提供一个函数指针。

这里有一些重构代码来说明:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX_STR_LEN 64

typedef struct Vector Vector;
typedef void (*vecinit_p)(Vector *,size_t idx,size_t count);

struct Vector {
    void *data;                         // pointer to array data
    size_t element_size;                // number of bytes in an array element
    size_t capacity;                    // number of elements allocated
    size_t size;                        // number of elements in use

    vecinit_p initfnc;                  // pointer to init function
};

// vecptr -- get pointer to i'th element
void *
vecptr(Vector *vec,size_t idx)
// idx -- index of desired element
{
    void *ptr;

    idx *= vec->element_size;

    ptr = vec->data;
    ptr += idx;

    return ptr;
}

// init_data -- initialize data elements
void
init_data(Vector *vec,size_t idx,size_t count)
// idx -- starting index
// count -- number of elements to initialize
{

    void *ptr = vecptr(vec,idx + 0);
    void *end = vecptr(vec,idx + count);
    memset(ptr,0,end - ptr);
}

// Allocate vector to initial capacity (block_size elements),
// Set element_size, size (to 0), capacity
void
init_vector(Vector *vec, size_t block_size, size_t element_size,vecinit_p fnc)
// block_size -- number of elements
// element_size -- number of bytes in a single element
{

    size_t new_len = block_size * element_size;
    vec->data = calloc(1,new_len);

    vec->element_size = element_size;
    vec->size = 0;
    vec->capacity = block_size;

    // provide a "default" constructor
    if (fnc == NULL)
        fnc = init_data;
    vec->initfnc = fnc;

    fnc(vec,0,vec->capacity);
}

// resize -- resize the array
void
resize(Vector *vec, size_t new_cap)
// new_cap -- desired new capacity
{

    // get byte length
    size_t new_len = new_cap * vec->element_size;

    void *data2 = malloc(new_len);

    if (data2 == NULL) {
        perror("malloc");
        exit(1);
    }

    vec->data = data2;

    // get old capacity and set new capacity
    size_t old_cap = vec->capacity;
    vec->capacity = new_cap;

    // initialize new elements
    if (new_cap > old_cap)
        vec->initfnc(vec,old_cap,old_cap - new_cap);
}

// vecpush -- append element to array
// RETURNS: pointer to "pushed" element
void *
vecpush(Vector *vec)
{

    // increase array capacity if needed
    if (vec->size >= vec->capacity)
        resize(vec,vec->capacity + 10);

    // point to element
    void *ptr = vecptr(vec,vec->size++);

    return ptr;
}

C 是一种为肾上腺素瘾君子准备的语言。它总是类似于自由攀登、跳伞和一级方程式赛车。 防御性编程并不是针对调用者各种“破坏”的完美保护,但总比没有好。

下面的代码正是本着这种精神。它无法检测到所有可能出错的地方(比如损坏的内存或者向量中的数据指针是否只是一个随机值),但它展示了如果用该语言编写应该使用的最少防御性编程。

C 具有“指针运算”这一特性。所以,如果你有uint16_t * p = 1000; 并且您访问 p + 1,它正在访问 1002(即 p + sizeof(uint16_t) * 1)。

这就是诀窍,您如何访问此类非常弱类型的向量中的元素。您将 void 指针转换为字节指针(作为示例),然后使用指针算法。

void* at(Vector* v, size_t index) {
  // C needs manual sanity checks for the preconditions
  if (NULL == v) return NULL;
  if (v->size <= index) return NULL;
  if (NULL == v->data) return NULL;
  // now we can be (reasonably, as much as we can tell) sure, we did not get garbage as arguments...
  return ((char*)(v->data)) + index * v->element_size;
}