(*(QUEUE **) &((*(q))[0])) 在libuv中是什么意思,或者队列是如何工作的?

What does (*(QUEUE **) &((*(q))[0])) mean in libuv, or How does the queue work?

我正在研究 C 中的 void 指针和双指针等,以尝试使事情变得动态。然后我遇到了 this ,它看起来如下:

typedef void *QUEUE[2];

#define QUEUE_NEXT(q)       (*(QUEUE **) &((*(q))[0]))
#define QUEUE_PREV(q)       (*(QUEUE **) &((*(q))[1]))

有太多的指针、括号和参考资料在我眼里。想知道是否有人可以解释:

这是如何工作的?具体来说,他们有这个:

#define QUEUE_INSERT_TAIL(h, q)                             \
  do {                                                      \
    QUEUE_NEXT(q) = (h);                                    \
    QUEUE_PREV(q) = QUEUE_PREV(h);                          \
    QUEUE_PREV_NEXT(q) = (q);                               \
    QUEUE_PREV(h) = (q);                                    \
  }                                                         \
  while (0)

QUEUE_INSERT_TAIL 是如何工作的?

或者,例如,我也有兴趣知道这是怎么回事:

#define QUEUE_INIT(q)                                                         \
  do {                                                                        \
    QUEUE_NEXT(q) = (q);                                                      \
    QUEUE_PREV(q) = (q);                                                      \
  }                                                                           \
  while (0)

...

QUEUE_INIT(&loop->wq);
QUEUE_INIT(&loop->idle_handles);
QUEUE_INIT(&loop->async_handles);
QUEUE_INIT(&loop->check_handles);
QUEUE_INIT(&loop->prepare_handles);
QUEUE_INIT(&loop->handle_queue);

最后,他们都在内部使用 QUEUE_NEXTQUEUE_PREV,做一些魔术。

这是一个循环链表的接口。当您解决第一个宏用法时,您会得到:

  • QUEUE_NEXT(&loop->wq) ->
  • *(QUEUE **) &((*( &loop->wq ))[0]) ->
  • *(QUEUE **) &(( loop->wq )[0]) ->
  • *(QUEUE **) &( loop->wq[0] ) ->
  • *(QUEUE **) &loop->wq[0]

实际上与 (QUEUE *)( loop->wq[0] ) 相同或只是 loop->wq[0]

这当然只有在 wqQUEUE 类型时才有效,它只不过是两个空指针的数组。作者诉诸 void* 因为据我所知,在 C 中不可能对指向自身的指针数组进行类型定义。

QUEUE_INSERT_TAIL btw 是拼接两个列表的代码。这个界面的有趣之处在于,您如何获得每个元素的内容?看一下 QUEUE_DATA

的定义
#define QUEUE_DATA(ptr, type, field)                                          \
  ((type *) ((char *) (ptr) - ((long) &((type *) 0)->field)))

及其用法

struct user_s {
  int age;
  char* name;

  QUEUE node;
};
user = QUEUE_DATA(q, struct user_s, node);

这解析为

  • QUEUE_DATA(q, struct user_s, node) ->
  • (struct user_s*) ((char *) (q) - ((long) &((struct user_s*) 0)->node))

通过减去其 QUEUE 成员的偏移量,有效地 returns 包含结构的地址,因此 q 的值被调整为结构的地址(此处user_s) 它指向。