C语言中的通用数据类型 [void *]

14

你好,我正在尝试在C语言中使用void *作为通用数据类型。我想要一个机制,可以用它存储任何东西并获取任何东西。我写了一些代码,但在最后一个测试案例中失败了。如果有人能够帮我看一下代码,或者有其他的想法,请告诉我。

我知道我要存储什么类型的数据,在那个时候我知道数据类型,但在检索时,我只知道起始地址和大小。

下面是代码:

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

} 
4个回答

19

最好使用您需要的所有类型的联合体,而不是void*,结合所选类型的枚举。 如果您没有有限的类型列表,则使用包含void*指针和至少分配大小的结构。

例如:

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

    void* value;
};

这使得你可以轻松地正确释放内存并制作通用代码,在对值进行操作之前可以分析类型。

你可以使用联合来实现类似的功能:

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

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

在联合体中,所有元素都使用相同的内存空间,只能访问其中一个元素。通过枚举,您知道应该访问哪个元素。当然,您没有像C++中那样的面向对象保护,因此需要确保在使用此结构的每个地方正确设置枚举。


@Benoit Thiery,您能否详细解释一下,如果您有任何示例,请提供。我对结构体解决方案很感兴趣。 - Avinash
谢谢Benoit,但我有有限的类型列表,所以您能否请更详细地介绍一下结构体方法。 - Avinash
@Avinash,即使您有一个有限的类型列表,联合仍然更好。联合几乎与结构相同。 - Prof. Falken
1
这被称为“变体”。它不一定比malloc更好,它是编写通用数据类型的一种非常浪费内存的方式。很少有语言支持自己的变体类型,这是有原因的。 - Lundin
@Avinash STL向量在内部处理动态内存分配并制作所有数据的硬拷贝。由于您的代码不复制任何数据,因此它更像是一种查找表而不是容器类。 - Lundin
显示剩余4条评论

5
你正在将整数值(55)复制到指针int_out中。无论地址55处的内容是什么(下一个4至8个字节),它都不太可能具有数字值55。相反,请尝试:
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);

你的代码还存在一个问题:在char_in/char_out部分,你只是复制指针值。虽然这样可以工作(在这种情况下),但这可能不是你真正想要的,或者说是吗?如果你真的想存储指针,那么可以跳过storeretrieve部分:
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

同样有效。另一方面,如果您想要存储字符串的实际内容,您必须将其复制(例如,在第二个示例中使用_strdup),并保存指向字符串副本的指针:

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

4

您正在覆盖指针,然后对其进行解引用...如果您的代码像这样,它将起作用...但这可能不是您想要的。

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

或者:

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

然而,在第二种情况下,这有点可疑,因为它假设'int'和'int*'具有相同的大小,因此在检索时可以将一个存储在另一个上面。我认为在所有系统上都不是这种情况。

1

Dirk 是完全正确的。 另一种表达方式是,没有分配空间来存放检索到的数据。

尝试:

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

当然,您可能想使用除malloc()之外的其他东西,并且没有检查以查看malloc()是否失败,但这应该可以让您走上正确的轨道。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接