Home > front end >  How to get the i-th element of vector
How to get the i-th element of vector

Time:05-15

I want to get the i-th element of the void*. I understand that it is void type and I have to give it a certain data type. The idea behind this is that it should be working for different data types. If i am right about the issue, how do I implement that?

#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;
}

CodePudding user response:

C is a language for adrenaline junkies. It's always akin to free climbing, sky diving and formula 1 racing. Defensive programming is not a perfect protection against all kinds of "sabotage" by a caller, but it is better than nothing.

The code below is in that very spirit. It cannot detect all possible things going wrong (like corrupted memory or if the data pointer in the vector is just a random value), but it showcases the least amount of defensive programming one should use if writing in that language.

C has "pointer arithmetic" as a feature. So, if you have e.g. a uint16_t * p = 1000; and you access p 1, it is accessing 1002 (i.e. p sizeof(uint16_t) * 1).

And this is the trick, how you can access an element in such a very weakly typed vector. You cast the void pointer to a byte pointer (as an example) and then use pointer arithmetic.

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;
}

CodePudding user response:

Your vector functions can not [meaningfully] access the array data using the index or pointer syntax such as:

vector->data[i]=0;

That is because it's a void * pointer but, more importantly, if element_size is (e.g.) 8, does that mean vector->data points to a double or unsigned long long?

Only the caller of your functions can do this:

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;

When you extend the array in resize, you can only mem* functions.

Replace your for loop with:

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

UPDATE:

There are some more issues. Although you can have capacity be a byte count, it is more usual for it to be an element count (just like size).

When I create such dynamic array/vector objects/functions, I usually do not have resize do initialization of the elements.

That's because it doesn't [really] know how to initialize the elements. For a c vector, the constructor knows.

So, if we wish resize [and init_vector] to do this, we need to provide a function pointer for this.

Here's some refactored code to illustrate:

#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;
}
  • Related