0

我的老师让我们使用以下预处理器宏在C中实现链接队列。这个想法是你通过让它不保存数据来使你的队列通用,然后你在其他地方有一个包装器结构,它保存队列中的节点,以及属于它的数据片段。

下面的宏从队列中获取一个节点(即node),wrapper结构的类型(即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)))
4

2 回答 2

1

只需将宏视为 C 的 sed

任何你看到 QUEUE_ENTRY(a, b, c) 的地方你都会得到:

((b *)((uint8_t*)(a) - offsetof(b, c)))

所有这些替换都是在编译之前完成的

于 2012-12-02T03:08:42.563 回答
1

C struct 中的成员元素的地址有固定的差异。这种差异是在编译时本身定义的。

您可以使用&(struct_object.member) - &struct_object. 这就是offsetof.

例如考虑以下结构:

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 等等。结构中的“填充”或“对齐”也起着决定偏移的作用。但是,这是在编译时决定的,而不是在运行时决定的。

因此,如果您有成员的地址(在我们的示例中为字符 c),但没有结构,则可以通过从成员地址中减去成员地址偏移量来获得父结构的地址。

在您的示例中,成员的地址包含在节点中。因此,如果您减去成员偏移量,您将获得容器结构的地址。

在 linux 内核源代码中,名称下可以使用相同的宏,container_of(如果有的话,我忘记了大写和下划线)。

于 2012-12-02T03:20:22.587 回答