Interpreting circular doubly linked list implementation which uses union

╄→гoц情女王★ 提交于 2019-12-11 14:37:12

问题


I am having some trouble interpreting this doubly-linked list:

struct _dnode {
    union {
        struct _dnode *head;
        struct _dnode *next;
    };
    union {
        struct _dnode *tail;
        struct _dnode *prev;
    };
};

typedef struct _dnode sys_dlist_t;
typedef struct _dnode sys_dnode_t;

And further functions are defined on this list, like, for example finding if given node is head of the list:

static inline int sys_dlist_is_head(sys_dlist_t *list, sys_dnode_t *node)
{
    return list->head == node;
}

Now, my questions are -

(i) Why would we need a union here? That too, in this specific way?

(ii) How come both the list and node of the list are going to be pointers of same type? (see typedef declaration)

(iii) If I declare a list of such a type, ie. the data_node items will be the elements of the sys_dlist_t type:

struct data_node{
    sys_dnode_t node;
    int data = 0;
} data_node[2];

and I declare a node such that:

sys_dnode_t *node = NULL;

and then if I want to iterate over my list to check if the data of the data_node element matches say, a number 3. Can I do that by typecasting node (which is currently a pointer to type sys_dnode_t) to a pointer to type data_node?

Now, in the code, this has been done, and it is like:

if (((struct data_node *)node)->data == 3) {
    break;
}

This bewilders me. I may have missed some code to figure this out, so please tell me if you need more information. Can we typecast a node pointer to point to some struct that contains node and then access other data of the struct? How does this work?

EDIT 1: Few more info on this list:

"The lists are expected to be initialized such that both the head and tail pointers point to the list itself. Initializing the lists in such a fashion simplifies the adding and removing of nodes to/from the list."

The initialization is as follows:

static inline void sys_dlist_init(sys_dlist_t *list)
{
    list->head = (sys_dnode_t *)list;
    list->tail = (sys_dnode_t *)list;
}

回答1:


(i) Why would we need a union here? That too, in this specific way?

So convenient, since this list is cyclic. The list structure is a pseudo node in the list. Therefore, the node can be viewed as a list structure and as node in a list.

Another definition might be:

union _dnode {
    struct {
        union _dnode *head;
        union _dnode *tail;
    };
    struct {
        union _dnode *next;
        union _dnode *prev;
    };
};

typedef union _dnode sys_dlist_t;
typedef union _dnode sys_dnode_t;

(ii) How come both the list and node of the list are going to be pointers of same type? (see typedef declaration)

This is also convenient to do, since these pointers refer to the same structure in memory.

(iii) If I declare a list of such a type, ie. the data_node items will be the elements of the sys_dlist_t type... Can I do that by typecasting node (which is currently a pointer to type sys_dnode_t) to a pointer to type data_node?

You can, because the pointer to the first field in the structure and the pointer to the structure are the same.

A node field need not be the first, but then a simple typecasting can not do that. For example:

struct list_elem {
    int foo;
    char *bar;
    ...
    sys_dnode_t siblings;
};

sys_dnode_t *node;
struct list_elem *elem;

elem = (struct list_elem *)((char *)node - offsetof(struct list_elem, siblings));

or if you define macro:

#define objectof(_ObjectT,_Field,x) \
    ((_ObjectT *)((char *)(x) - offsetof(_ObjectT,_Field)))

elem = objectof(struct list_elem, siblings, node);


来源:https://stackoverflow.com/questions/49642853/interpreting-circular-doubly-linked-list-implementation-which-uses-union

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!