我的老师让我们使用以下预处理器宏来实现 C 中的链接队列。我们的想法是让你的队列保持通用状态,不让数据保存,然后在其他地方有一个包装器结构,它将节点保存在队列中,以及属于它的数据片段。
下面的宏从队列中接收一个节点(即节点),包装器结构的类型(即struct Wrapper),以及Wrapper的队列节点元素的名称(即qnode(Wrapper有一个名为qnode的元素) ))。
然后宏返回传入节点的struct Wrapper。
所以电话会是这样的:
queue_entry(node, struct Wrapper, qnode)
这在我眼中非常酷,而且效果很好,(就像老师写的那样,它会更好!)。 但我希望有人可以向我解释它是如何运作的?因为我对幕后的实际情况感到茫然。
宏:
#define queue_entry(NODE, STRUCT, MEMBER) \
((STRUCT *)((uint8_t*)(NODE) - offsetof(STRUCT, MEMBER)))
答案 0 :(得分:1)
将宏视为C
的sed你会看到QUEUE_ENTRY(a,b,c)的任何地方:
((b *)((uint8_t *)(a) - offsetof(b,c)))
所有这些替换都是在编译之前
完成的答案 1 :(得分:1)
C struct中的成员元素在其地址上有固定的差异。这种差异在编译时自己定义。
您可以使用&(struct_object.member) - &struct_object
获取地址差异。这是offsetof
返回的内容。
e.g。请考虑以下结构:
struct abcd{
int a; // 4 bytes
int b; // another 4 bytes
char c;// 1 byte
}
然后offsetof(struct abcd,c)
将返回8. offsetof(struct abcd,a)
将为0&等等。在确定偏移量时,结构中的“padding”或“对齐”也起到了作用。但是,这是在编译时决定的,而不是运行时。
因此,如果您有成员的地址(在我们的示例中为char c),而不是结构,则可以通过从成员地址中减去成员地址偏移量来获取父结构的地址。
在您的示例中,成员的地址包含在节点中。因此,如果你减去member ofset,你将获得容器结构的地址。
在linux内核源代码中,名称为container_of
的同一个宏可用(我忘了大写和下划线,如果有的话)。