【问题标题】:Interpreting circular doubly linked list implementation which uses union解释使用联合的循环双向链表实现
【发布时间】:2018-09-13 12:49:18
【问题描述】:

我在解释这个双向链表时遇到了一些麻烦:

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;

并且在这个列表上定义了更多的函数,例如,查找给定节点是否是列表的头部:

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

现在,我的问题是 -

(i) 为什么我们需要一个联合?那也是,以这种特定的方式?

(ii) 为什么列表中的listnode 都是相同类型的指针? (见typedef声明)

(iii) 如果我声明这种类型的列表,即。 data_node 项目将是 sys_dlist_t 类型的元素:

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

我声明了一个节点:

sys_dnode_t *node = NULL;

然后如果我想遍历我的列表以检查 data_node 元素的 data 是否匹配例如数字 3。我可以通过类型转换来做到这一点 node (目前是一个指向类型的指针sys_dnode_t) 指向类型为data_node的指针?

现在,在代码中,这已经完成了,就像:

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

这让我很困惑。我可能错过了一些代码来解决这个问题,所以如果您需要更多信息,请告诉我。我们可以将node 指针类型转换为指向包含node 的某个结构,然后访问该结构的其他数据吗?这是如何工作的?

编辑 1: 关于此列表的更多信息:

“列表应该被初始化,这样头和尾指针都指向列表本身。以这种方式初始化列表可以简化向列表添加和删除节点。”

初始化如下:

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

【问题讨论】:

  • 对于iiidata_node 结构是在 C 中模拟继承的一种方式。
  • 您确定sys_dlist_is_head 应该与tail 进行比较吗?
  • @freestyle 哦,你是对的。让我纠正一下
  • 在循环双向链表中,这样做非常方便,因为您可以将列表结构视为伪节点。但从你的描述来看,我不明白这个列表是否是循环的。你能显示列表初始化吗?
  • @freestyle 我已经添加了初始化部分,如果我错了请纠正我,但它看起来确实像一个循环的dll

标签: c pointers doubly-linked-list


【解决方案1】:

(i) 为什么我们需要一个联合?那也是,以这种特定的方式?

很方便,因为这个列表是循环的。列表结构是列表中的一个伪节点。因此,节点可以看作是一个列表结构,也可以看作是列表中的节点。

另一个定义可能是:

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) 为什么链表和链表的节点都是相同类型的指针? (见 typedef 声明)

这样做也很方便,因为这些指针指向内存中的相同结构。

(iii) 如果我声明这种类型的列表,即。 data_node 项将是 sys_dlist_t 类型的元素...我可以通过将 node(当前是指向类型 sys_dnode_t 的指针)类型转换为指向类型 data_node 的指针来做到这一点吗?

可以,因为指向结构中第一个字段的指针和指向结构的指针是一样的。

节点字段不必是第一个,但简单的类型转换不能做到这一点。例如:

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

或者如果你定义宏:

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

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

【讨论】:

  • 哇,谢谢你的详细解释,我现在清楚了。所以如果它不是第一个字段,那么我需要在类型转换时使用offsetof,对吧?
  • 是的。而且。如果它是第一个提交的,那么offsetof 也可以工作,因为offsetof 将是 0。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-31
  • 2021-07-05
  • 1970-01-01
  • 2013-11-15
  • 1970-01-01
  • 2021-11-18
  • 2011-06-03
相关资源
最近更新 更多