16
votes

plain C have nice feature - void type pointers, which can be used as pointer to any data type.
But, assume I have following struct:


struct token {
    int type;
    void *value;
};

where value field may point to char array, or to int, or something else.
So when allocating new instance of this struct, I need:

1) allocate memory for this struct;
2) allocate memory for value and assign it to value field.

My question is - is there ways to declare "array of type void", which can be casted to any another type like void pointer?

All I want is to use "flexible member array" (described in 6.7.2.1 of C99 standard) with ability to casting to any type.

Something like this:


struct token {
    int type;
    void value[];
};

struct token *p = malloc(sizeof(struct token) + value_size);
memcpy(p->value, val, value_size);
...
char *ptr = token->value;

I suppose declaring token->value as char or int array and casting to needed type later will do this work, but can be very confusing for someone who will read this code later.

6
using char[] is fine imho, since sizeof(char) == 1 and you will never get surprised. You may want to consider macros to access p->value with the correct type.Alexandre C.

6 Answers

5
votes

Well, sort of, but it's probably not something you want:

struct token {
  // your fields
  size_t item_size;
  size_t length
};

struct token *make_token(/* your arguments */, size_t item_size, size_t length)
{
    struct token *t = malloc(sizeof *t + item_size * length);
    if(t == NULL) return NULL;
    t->item_size = item_size;
    t->length    = length;
    // rest of initialization
}

The following macro can be used to index your data (assuming x is a struct token *):

#define idx(x, i, t) *(t *)(i < x->length ? sizeof(t) == x->item_size ?
                       (void *)(((char *)x[1]) + x->item_size * i)
                     : NULL : NULL)

And, if you like, the following macro can wrap your make_token function to make it a little more intuitive (or more hackish, if you think about it that way):

#define make_token(/* args */, t, l) (make_token)(/* args */, sizeof(t), l)

Usage:

struct token *p = make_token(/* args */, int, 5); // allocates space for 5 ints
...
idx(p, 2, int) = 10;
2
votes

Expanding on AShelly's answer you can do this;

/** A buffer structure containing count entries of the given size. */
typedef struct {
    size_t size;
    int count;
    void *buf;
} buffer_t;

/** Allocate a new buffer_t with "count" entries of "size" size. */
buffer_t *buffer_new(size_t size, int count)
{
    buffer_t *p = malloc(offsetof(buffer_t, buf) + count*size);
    if (p) {
        p->size = size;
        p->count = count;
    }
    return p;
}

Note the use of "offsetof()" instead of "sizeof()" when allocating the memory to avoid wasting the "void *buf;" field size. The type of "buf" doesn't matter much, but using "void *" means it will align the "buf" field in the struct optimally for a pointer, adding padding before it if required. This usually gives better memory alignment for the entries, particularly if they are at least as big as a pointer.

Accessing the entries in the buffer looks like this;

/** Get a pointer to the i'th entry. */
void *buffer_get(buffer_t *t, int i)
{
    return &t->buf + i * t->size;
}

Note the extra address-of operator to get the address of the "buf" field as the starting point for the allocated entry memory.

1
votes

I would probably do this:

struct token {
    int type;
    void *value;
};

struct token p;

p.value = malloc(value_size);

p.value[0] = something;
p.value[1] = something;
...

edit, actually you have to typecast those p.value[index] = somethings. And/or use a union to not have to typecast.

1
votes

You can't have an array of 'void' items, but you should be able to do something like what you want, as long as you know value_size when you do the malloc. But it won't be pretty.

struct token {
        int type;
        void *value;       
};    

value_size = sizeof(type)*item_count;
struct token *p = malloc(sizeof(struct token) + value_size);
//can't do memcpy:  memcpy(p->value, val, value_size);  
//do this instead
type* p = (type*)&(p->value);
type* end = p+item_count;
while (p<end) { *p++ = someItem; }

Note that you need an extra address-of operator when you want to get the extra storage.

type *ptr = (type*)&(token->value); 

This will 'waste' sizeof(void*) bytes, and the original type of value doesn't really matter, so you may as well use a smaller item. I'd probably typedef char placeholder; and make value that type.

0
votes

following structure can help you.

struct clib_object_t {
void* raw_data;
size_t size;
};

struct clib_object_t*
new_clib_object(void *inObject, size_t obj_size) {
    struct clib_object_t* tmp = (struct clib_object_t*)malloc(sizeof(struct clib_object_t));   
    if ( ! tmp )
        return (struct clib_object_t*)0;
    tmp->size        = obj_size;
    tmp->raw_data    = (void*)malloc(obj_size);
    if ( !tmp->raw_data ) {
        free ( tmp );
        return (struct clib_object_t*)0;
    }
    memcpy ( tmp->raw_data, inObject, obj_size);
    return tmp;
}

clib_error
get_raw_clib_object ( struct clib_object_t *inObject, void**elem) {
    *elem = (void*)malloc(inObject->size);
    if ( ! *elem )
        return CLIB_ELEMENT_RETURN_ERROR;
    memcpy ( *elem, inObject->raw_data, inObject->size );

    return CLIB_ERROR_SUCCESS;
}

More Details : clibutils

0
votes

Array of type void is not supporting in c/c++. Example like:

int main() {

 void  alexa[]; // error: declaration of ‘alexa’ as array of void 

return 0;

}

Array of void pointer is supported in c/c++. Example below:

int main(int argc, char argv*[]) 
{

void *alexa[100]; // Compiled successfully

return 0;

}