预处理器宏解释?

时间:2012-12-02 02:53:14

标签: c struct linked-list c-preprocessor

我的老师让我们使用以下预处理器宏来实现 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)))

2 个答案:

答案 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的同一个宏可用(我忘了大写和下划线,如果有的话)。