2011-04-25 17 views
6

plain C tiene una función agradable - punteros de tipo void, que se pueden utilizar como puntero a cualquier tipo de datos.
Pero, supongamos que he siguiente estructura:matriz de tipo void


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

donde el campo valor puede apuntar a char matriz o a int, o alguna otra cosa.
Así que cuando se asigna una nueva instancia de esta estructura, necesito:

1) asignar memoria para esta estructura;
2) asignar memoria para el valor y asignarla al campo de valor.

Mi pregunta es - ¿hay formas de declarar "gama de tipo void", que se puede lanzar a cualquier otro tipo como puntero vacío?

Todo lo que quiero es usar "arreglo de miembro flexible" (descrito en 6.7.2.1 del estándar C99) con capacidad para transmitir a cualquier tipo.

Algo como esto:


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; 

supongo que declara tokens> valor como char o int matriz y echando a tipo necesario más adelante va a hacer este trabajo, pero puede ser muy confuso para alguien que va a leer esto código más tarde.

+0

¿Doble punteros? –

+2

usando 'char []' está bien imho, ya que 'sizeof (char) == 1' y nunca te sorprenderá. Es posible que desee considerar las macros para acceder a 'p-> value' con el tipo correcto. –

Respuesta

1

que probablemente hacer esto:

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

struct token p; 

p.value = malloc(value_size); 

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

edición, en realidad hay que encasillarse aquellos Valor PD [índice] = tantos. Y/o use una unión para no tener que encasillar.

+0

Tendría que lanzar 'p.value' antes de usarlo. –

+0

'p.value [0] = something' arrojará un error en tiempo de compilación, ¿no? No se puede desreferenciar un 'void *'. –

+0

sonríe, me ganaste ... intentaba editarlo rápidamente. –

3

Bueno, más o menos, pero probablemente no es algo que desea:

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 
} 

La siguiente macro se puede utilizar para indexar sus datos (suponiendo x es un 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) 

Y , si lo desea, la siguiente macro puede ajustar su función make_token para hacerlo un poco más intuitivo (o más hackish, si lo piensa de esa manera):

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

Uso:

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

No se puede tener una gran variedad de artículos 'vacío', sino que debe ser capaz de hacer algo como lo que quiere, como siempre que se sepa value_size cuando hace lo malloc. Pero no será bonito.

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

Tenga en cuenta que necesita un operador de dirección adicional cuando desea obtener el almacenamiento adicional.

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

Esto 'perder' sizeof bytes (* void), y el tipo original de value realmente no importa, lo que puede también utilizar un elemento más pequeño. Probablemente typedef char placeholder; y haga value ese tipo.

0

siguiente estructura puede ayudarlo.

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

Más detalles: clibutils

0

Ampliando la respuesta de AShelly Usted puede hacer esto;

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

Nota el uso de "offsetof()" en lugar de "sizeof()" en la asignación de la memoria para evitar perder el "void * buf;" tamaño del campo. El tipo de "buf" no importa mucho, pero usar "void *" significa que alineará el campo "buf" en la estructura de manera óptima para un puntero, agregando relleno antes si es necesario. Esto generalmente proporciona una mejor alineación de memoria para las entradas, particularmente si son al menos tan grandes como un puntero.

El acceso a las entradas en el búfer se ve así;

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

Nota del operador de dirección adicional para obtener la dirección del campo "buf" como punto de partida para la memoria de entrada asignado.