2011-04-05 17 views
7

Hola, estoy tratando de usar void * como un tipo de datos genérico en C. Lo que quiero es un mecanismo con el que pueda almacenar cualquier cosa y obtener cualquier cosa. Escribí un código, pero está fallando en el último caso. ¿Puede alguien echar un vistazo al código? Si tiene alguna otra idea, por favor hágamelo saber.Tipo de datos genéricos en C [void *]

Sé lo que intento almacenar, así que en ese momento conozco el tipo de datos, pero durante el nuevo examen solo conozco la dirección y el tamaño de inicio.

Aquí está el código:

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


static void store(void *destination, void *source, size_t size) { 
    memcpy ((char*)destination, source, size); 
} 
static void retrieve (void *destination, void *source, size_t size) { 
    memcpy (destination, (char*)source, size); 
} 

void *storage_char_ptr = (void*) malloc (sizeof(char*)); 
void *storage_int_ptr = (void*) malloc (sizeof(int*)); 
void *storage_int  = (void*) malloc (sizeof(int)); 

int main() { 

    int int_in = 65; 
    void *int_out_ptr; 
    int *ptr = (int*) malloc (sizeof(int)); 
    memcpy (ptr, &int_in, sizeof(int)); 

    store (storage_int_ptr, &ptr, sizeof(int*)); 
    retrieve (&int_out_ptr, storage_int_ptr, sizeof(int*)); 
    assert (int_in == *(int*)int_out_ptr); 

    char *char_in = "HelloWorld!!"; 
    void *char_out; 
    store (storage_char_ptr, &char_in, sizeof(char*)); 
    retrieve (&char_out, storage_char_ptr, sizeof(char*)); 
    assert (strcmp (char_in, (char*)char_out) == 0); 

    char_in = _strdup("HelloWorld!!"); 
    store (storage_char_ptr, &char_in, sizeof(char*)); 
    retrieve (&char_out, storage_char_ptr, sizeof(char*)); 
    assert (strcmp (char_in, (char*)char_out) == 0); 

    /* This is where it is failing */ 
    int_in = 55; 
    void* int_out; 
    store (storage_int, &int_in, sizeof(int)); 
    retrieve (&int_out, storage_int, sizeof(int)); 
    assert (55 == *(int*)int_out); 

} 
+2

¿cómo está fallando? – MByD

Respuesta

9

Es mucho mejor utilizar una unión de todos los tipos que necesitará en lugar de void*, combinada con una enumeración del tipo seleccionado. Si no tiene una lista finita de tipos, utilice una estructura que contenga el puntero void* y el tamaño asignado como mínimo.

Por ejemplo:

struct ANYTYPE 
{ 
    enum { 
     typUndefined, 
     typInt,   // 1 
     typUint, 
     typString, 
     typByteString, 
     typLong,   // 5 
     typFloat, 
     typDouble, 
    } iType; 

    void* value; 
}; 

Esto le permite fácilmente de desasignar la memoria correctamente y hacer que el código genérico que puede analizar el tipo antes de actuar sobre el valor. Usted tiene una opción similar con el sindicato:

struct ANYTYPE 
{ 
    enum { 
     typUndefined, 
     typInt,   // 1 
     typUint, 
     typString, 
     typLong 
    } iType; 

    union 
    { 
     int i; 
     unsigned int u; 
     char* s; 
     long l; 
    } value; 
} 

En una unión, todos los elementos están utilizando mismo espacio de memoria y sólo uno se debería acceder. A través de la enumeración, usted sabe a qué elemento debe accederse. Por supuesto, no tiene protección OO como en C++, por lo que debe asegurarse de establecer correctamente la enumeración donde quiera que use esta estructura.

+0

@Benoit Thiery, ¿podría explicar más, si tiene algún ejemplo? Estoy interesado en la solución struct. – Avinash

+0

gracias Benoit, pero tengo una lista finita de tipos, así que podrías por favor arrojar más luz sobre el enfoque struct. – Avinash

+0

@Avinash, incluso si tiene una lista limitada de tipos, la unión es aún mejor. Una unión es casi lo mismo que una estructura. –

5

está copiando el valor entero (55) en el puntero int_out. Cualquiera que sea la mentira al (los próximos 4 a 8 bytes) en el frente 55, es poco probable que tenga el valor numérico 55. En su lugar, tratar

int int_in = 55; 
int int_out; 
store (storage_int, &int_in, sizeof(int)); 
retrieve (&int_out, storage_int, sizeof(int)); 
assert (55 == int_out); 

Otro problema con el código: en los char_in/char_out partes, que son solo copiando valores de puntero. Esto funciona (en este caso), pero no es lo que realmente pretendías, ¿o no? Si realmente desea almacenar el puntero, puede omitir la parte store y retrieve:

char* str_in = "hello, world";  // Original value 
void* generic_ptr = str_in;  // ... a pointer to which is stored in some data structure 
char* str_out = generic_ptr;  // ... which can later be retrieved 

funcionará tan bien. Por otro lado: si va a almacenar el contenido real de la cadena, hay que copiarlo (como, con _strdup como en el segundo ejemplo), y guardar el puntero a la copia de la cadena:

char* str_in = _strdup("copy me"); 
void* generic_ptr = str_in;   // Gets stored in your data structures 
4

Está sobreescribiendo el puntero, luego desreferenciando ... si su código se viera así funcionaría ... pero puede que eso no sea lo que busca.

int int_out; 
    store (storage_int, &int_in, sizeof(int)); 
    retrive (&int_out, storage_int, sizeof(int)); 
    assert (55 == int_out); 

o:

int *int_out; 
    store (storage_int, &int_in, sizeof(int)); 
    retrive (&int_out, storage_int, sizeof(int)); 
    assert (55 == (int)int_out); 

Sin embargo, en el segundo caso se trata de un poco dudosa, porque se supone que tanto 'int' y 'int *' son del mismo tamaño, por lo que está capaz de almacenar uno encima del otro al recuperar. Creo que este no es el caso en todos los sistemas.

1

Dirk es totalmente correcto. Otra forma de expresar esto, es que no hay espacio asignado para poner los datos recuperados en

intento:.

int_in = 55; 
void* int_out = malloc (sizeof (int_in)); 
store (storage_int, &int_in, sizeof(int)); 
retrieve (int_out, storage_int, sizeof(int)); 
assert (55 == *(int*)int_out); 

Por supuesto, es posible que desee utilizar algo distinto de malloc(), y hay no se comprueba si malloc() falló, pero esto debería llevarlo por el camino correcto.