我在写 C 时通常忽略了使用宏,但我想我知道它们的基本原理。当我在linux内核中阅读list的源代码时,我看到了类似的东西:
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
(您可以从here.)
访问代码的剩余部分我在LIST_HEAD_INIT中并不理解&符号的功能(我不认为它们是操作数的地址),因此在代码中使用了LIST_HEAD_INIT。如果有人能够启发我,我会很感激。
答案 0 :(得分:15)
要知道实际发生了什么,我们需要定义struct list_head
:
struct list_head {
struct list_head *next, *prev;
};
现在考虑一下宏:
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
如果在代码中我写LIST_HEAD(foo)
,它会扩展为:
struct list_head foo = { &(foo) , &(foo)}
表示带有标头节点的空双向链表,其中next
和prev
指针指向标头节点本身。
与做:
相同struct list_head foo;
foo.next = &foo;
foo.prev = &foo;
因此,这些宏有效地提供了一种初始化双向链表的方法。
是的,这里使用&
作为运营商的地址。
修改强>
在您提供的链接中。你有:
struct list_head test = LIST_HEAD (check);
这是不正确的。你应该:
LIST_HEAD (check);
答案 1 :(得分:8)
每次你对宏实际上在做什么都有疑问时,你可以让'gcc -E'为你扩展它。
在这种情况下,它只是初始化一个包含指向自身的列表结构。
答案 2 :(得分:3)
他们是这里的运营商地址。内核中的链表避免使用空指针来表示列表的结尾。因此,必须使用一些有效指针初始化标头。在此实现中,如果“head”或“tail”指针指向列表标题的地址,则该列表被视为空。
答案 3 :(得分:1)
宏本身似乎没有在list.h
中使用。
我认为&
的确代表了代码中的地址。 struct list_head
包含两个指针,因此声明struct list_head name = LIST_HEAD_INIT(name)
(请参阅宏LIST_HEAD
)获取一对指针作为初始化器应该是很自然的。
答案 4 :(得分:0)
这正是它们的原因,&符号取参数的地址并将它们存储在“head”和“next”列表字段中。无法告诉你为什么,因为我没有找到这个宏的实际用途,但这就是它的作用。