(*(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]))
有太多的指针、括号和参考资料在我眼里。想知道是否有人可以解释:
(*(QUEUE **) &((*(q))
部分在做什么,
- 这个队列怎么只能有两个项目。
这是如何工作的?具体来说,他们有这个:
#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_NEXT
和 QUEUE_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]
这当然只有在 wq
是 QUEUE
类型时才有效,它只不过是两个空指针的数组。作者诉诸 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
) 它指向。
我正在研究 C 中的 void 指针和双指针等,以尝试使事情变得动态。然后我遇到了 this ,它看起来如下:
typedef void *QUEUE[2];
#define QUEUE_NEXT(q) (*(QUEUE **) &((*(q))[0]))
#define QUEUE_PREV(q) (*(QUEUE **) &((*(q))[1]))
有太多的指针、括号和参考资料在我眼里。想知道是否有人可以解释:
(*(QUEUE **) &((*(q))
部分在做什么,- 这个队列怎么只能有两个项目。
这是如何工作的?具体来说,他们有这个:
#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_NEXT
和 QUEUE_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]
这当然只有在 wq
是 QUEUE
类型时才有效,它只不过是两个空指针的数组。作者诉诸 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
) 它指向。