在struct中使void *指向一个整数

时间:2013-05-19 21:43:09

标签: c ansi void-pointers

我有一个结构如下:

typedef struct Node {
    void* data;
    unsigned long id;
    NodePtr next;
    NodePtr prev;
} Node;

它意味着是链表ADT中的节点。我有2个不同的构造函数,具体取决于Node需要在数据中保存的内容。一个构造函数使用:

NodePtr TempNode;
TempNode = malloc( sizeof(Node) );
/* Other stuff */
TempNode->data = newList();
return (TempNode);

这似乎可以让我通过返回(List-> current->数据)来访问该列表,其中current是List Struct中的Node指针

但是,我想创建一个构造函数的版本,其中(data)指向一个int。我已经读过,我可以通过以下方式做到这一点

void* ptr;
int x = 0;
*((int*)ptr) = x;

但是我的构造函数设置方式,这意味着我必须做这样的事情?

*((int*)TempNode->data) = 1; // data starts at 1

这不起作用。我对C很新,所以我不太懂术语。我读到解除引用(使用 - >符号?)不能用void *来完成,但它似乎在我的构造函数的列表版本中工作正常。如何重写我的其他构造函数以将此void *转换为int?

3 个答案:

答案 0 :(得分:2)

我强烈反对这样做,但如果您真的想使用void *成员来保存整数,您可以这样做:

Node *constructor_int(int n)
{
    Node *tmp = malloc(sizeof(*tmp));
    /* Other stuff */
    tmp->data = (void *)n;
    return(tmp);
}

这涉及最小数量的演员表,避免了大多数类型相对大小的问题。

这种明显的逻辑方法是为data成员分配一个整数,指向:

Node *constructor_int(int n)
{
    Node *tmp = malloc(sizeof(*tmp));
    /* Other stuff */
    tmp->data = malloc(sizeof(int));
    *(int *)temp->data = n;
    return(tmp);
}

你必须记住释放两个内存分配。

在使用结果之前,代码还应检查内存分配是否成功。

答案 1 :(得分:1)

我们来谈谈这个

当您执行类似

的操作时
NodePtr TempNode;
TempNode = malloc( sizeof(Node) );

你已经要求图书馆重新设置一些足够Node的动态存储空间。该内存的初始值是未定义的,所以现在指针TempNode->data可以指向任何地方,并且可能没有指向保留供您使用的内存。

当你这样做时

TempNode->data = newList();

你给指针一个(大概是newList()做一些合法且合理的)有效值;

如果你做了

*((int*)TempNode->data) = 1;

指示编译器

  1. TempNode->Data视为指向int的指针,
  2. 取消引用它和
  3. 将另一端的每个内存的值设置为1(注意,在任何时候都没有设置data本身,只要它指向的是什么)
  4. 但你不知道它指向的是什么! 取消引用它是未定义的行为,严重是坏事。

    除非指向指向您有权使用的内存,否则您始终有责任确保不取消引用指针。

答案 2 :(得分:1)

如何将数据指向我可以使用的区域?”

我不确定你的意思是否我将在下面解释(并且它不会简短:P),但如果你问如何区分{{1中存储的数据的类型然后用那个实现你不能。

你把它留给最终程序员来记住他存储在列表中的数据类型(这不是坏事btw ..相反,它是常态)。换句话说,你相信最终程序员在打印Node->data时会应用适当的演员。

如果由于某种原因您希望为列表提供更多托管API,则可以在Node->data结构中添加一个字段,以标识列表中存储的数据类型。

例如......

List

当然,您可以自由支持比上面enum DataType { DT_INVALID = 0, DT_PTR DT_CHAR, DT_INT, DT_FLOAT, DT_DOUBLE, ... /* not a data-type, just their total count */ DT_MAX }; #define DT_IS_VALID(dt) ( (dt) > DT_INVALID && (dt) < DT_MAX ) typedef struct List List; struct List { enum DataType dt; Node *head; }; 中列出的数据类型更少或更多的数据类型(即使是自定义数据类型,比如字符串,或根据您的项目认为合适的任何数据类型)。

首先,您需要一个构造函数(或初始化程序)来获取列表,如下所示......

enum

并实例化它,就像这样......

List *new_list( enum DataType dt )
{
    List *ret = NULL;

    if ( !DT_IS_VALID(dt) )
        return NULL;

    ret = malloc( sizeof(List) );
    if ( NULL == ret )
        return NULL;

    ret->dt = dt; 
    ret->head = NULL;

    return ret;
}

现在您已经将预期的数据类型存储到列表元数据中,您可以自由选择如何实现int main( void ) { List *listInt = new_list( DT_INT ); if ( NULL == list ) { /* handle failure here */ } 构造函数。例如,通用的可能看起来像这样......

Node

对于int list_add_node( List *list, const void *data ) { Node *node = NULL; size_t datasz = 0; /* sanity checks */ if ( !list || !data || !DT_IS_VALID(list->dt) ) return 0; /* false */ node = malloc( sizeof(Node) ); if ( NULL == node ) return 0; /* false */ /* when data points to mem already reserved say for an array (TRICKY) */ if ( DT_PTR == list->dt ) { node->data = data; } /* when data points to mem reserved for a primitive data-type */ else { datasz = dt_size( list->dt ); /* implement dt_size() according to your supported data-types */ node->data = malloc( datasz ); if ( NULL == node->data ) { free( node ); return 0; /* false */ } memcpy(node->data, data, datasz ); } /* add here the code dealing with adding node into list->head */ ... return 1; /* true */ } (我在示例中标记为 TRICKY ),实现不同的DT_PTR构造函数更安全,可能接受2个额外的参数,让我们说{ {1}}和Node,因此函数可以分配elemsz个字节用于将nelems内容复制到它们中,以防elemsz * nelems指向数组,结构或任何内容其他非原始类型。或者,您可以专门为数组提供额外的data枚举值。你可以自由地做任何最适合你的事。

在任何情况下,对于data,上面的示例依赖于DT_ARR的调用者正确分配了传递的DT_PTR,并且在一般情况下,这是而不是一件好事。

代码更复杂,但您知道列表中存储的数据类型。因此,至少可以添加的原始数据类型表示根据list_add_node自动转换其输出的打印例程(对于非原始数据类型,您应该通过回调函数提供对自定义打印例程的支持)。

您甚至可以将其发挥到极致,并将data字段从list->dt移至dt。在这种情况下,您在节点中实现了异构数据的列表,但它变得更加复杂,而且很少有用(如果有的话)。

所有这些ADT东西通过(void *)指针都存在严重的性能问题,这就是为什么速度关键实现会利用(或者如果你愿意的话滥用)预处理器而不是这种东西。