侵入式链表将节点指针嵌入数据结构内部,减少内存分配并提升缓存性能。其核心是list_entry宏,通过offsetof从节点指针反推宿主结构地址,实现高效遍历与操作。

实现一个侵入式链表的关键在于将节点信息直接嵌入到数据结构内部,而不是由链表容器额外分配节点。这种方式减少了内存分配次数,提升了缓存命中率,常用于高性能场景如内核开发或实时系统。
什么是侵入式链表
在非侵入式链表中,链表节点包含指向实际数据的指针;而在侵入式链表中,数据结构本身包含链表所需的指针成员(如 next 和 prev)。这意味着对象必须“知道”自己正被用于链表管理。
优点包括:
- 减少内存分配:无需为链表节点单独申请空间
- 提高性能:更好的局部性,减少指针跳转
- 确定性:适用于不允许动态分配的环境
基本结构设计
定义一个基础的链表节点结构,通常作为其他结构的成员:
立即学习“C++免费学习笔记(深入)”;
struct list_node {
struct list_node* next;
struct list_node* prev;
};
define list_entry(ptr, type, member) \
((type*)((char*)(ptr) - offsetof(type, member)))
list_entry 是关键宏,用于从 node 指针反推出宿主结构体的地址。它依赖于 offsetof,计算 member 在 type 中的偏移。
使用示例:
struct Person {
int id;
char name[32];
struct list_node node; // 链表节点嵌入
};
链表操作实现
实现基本的初始化、插入和删除操作:
void list_init(struct list_node* head) {
head->next = head;
head->prev = head;
}
void list_insert(struct list_node prev, struct list_node next, struct list_node* new_node) {
new_node->next = next;
new_node->prev = prev;
prev->next = new_node;
next->prev = new_node;
}
void list_add_tail(struct list_node head, struct list_node new_node) {
list_insert(head->prev, head, new_node);
}
void list_del(struct list_node* node) {
node->prev->next = node->next;
node->next->prev = node->prev;
node->next = nullptr;
node->prev = nullptr;
}
这些是底层操作。添加元素时,直接传入其 node 成员地址即可。
遍历链表并访问宿主结构:
struct list_node* curr;
for (curr = head.next; curr != &head; curr = curr->next) {
struct Person* p = list_entry(curr, struct Person, node);
printf("ID: %d, Name: %s\n", p->id, p->name);
}
优化与注意事项
侵入式链表虽高效,但也有一些限制和优化点:
- 一个对象只能属于一种链表类型(除非定义多个 node 成员)
- 结构体内存布局需明确,避免 padding 影响 offsetof
- 可扩展支持双链表、单链表、无锁链表等变种
- 结合模板(C++)可提升类型安全,避免宏出错
C++ 中可用模板封装,例如:
templateclass intrusive_list { struct Node { Node* next; Node* prev; }; Node* head; public: void add(T* obj) { Node* node = &obj->node; // 假设 T 有 node 成员 // 插入逻辑 } };
基本上就这些。侵入式链表适合对性能敏感的场景,设计上更贴近硬件,但牺牲了一定灵活性。理解其原理有助于写出更高效的 C/C++ 数据结构。











