普通的双向循环链表,一般将前向指针
p
r
e
v
prev
prev以及后向指针
n
e
x
t
next
next内嵌到特定的数据结构中。
再根据这种特定的结构编写插入,删除等函数。
struct myOrdinaryType {
int data;
struct myOrdinaryType *prev, *next;
};
这里介绍一种通用的双向循环链表,可以不依赖具体的数据类型。
struct list_entry_t {
struct list_entry_t *prev, *next;
};
struct myType{
int data;
list_entry_t list_entry;
};
如图所示,将
l
i
s
t
_
e
n
t
r
y
_
t
list\_entry\_t
list_entry_t类型作为自定义类型的一个成员,内嵌到结构体中。
注意箭头的指向,上图中指针指向的是整体,该图中指针指向的是成员。
注意
l
i
s
t
_
e
n
t
r
y
_
t
list\_entry\_t
list_entry_t类型是通用的,不需要依赖特定的数据类型。
初始化操作,直接将节点的前向指针和后向指针指向自己。
static inline void list_init(list_entry_t* elm) {
elm->prev = elm->next = elm;
}
下面是两个基础的插入,删除操作。
static inline void __list_add(list_entry_t* elm, list_entry_t* prev, list_entry_t* next) {
prev->next = next->prev = elm;
elm->next = next;
elm->prev = prev;
}
static inline void __list_del(list_entry_t* prev, list_entry_t* next) {
prev->next = next;
next->prev = prev;
}
插入,删除操作,对基础操作的简单封装。
static inline void list_add(list_entry_t* listelm, list_entry_t* elm) {
__list_add(elm, listelm, listelm->next);
}
static inline void list_del(list_entry_t* listelm) {
__list_del(listelm->prev, listelm->next);
}
由于
l
i
s
t
_
e
n
t
r
y
_
t
list\_entry\_t
list_entry_t类型指针为通用指针,无法通过其访问特定数据类型中的数据。
因此需要将其转化成特定数据类型指针。
#define offsetof(type, member) \
((size_t)(&((type *)0)->member))
#define to_struct(ptr, type, member) \
((type *)((char *)(ptr) - offsetof(type, member)))
// 宏定义,将list_entry_t类型指针转化为特定类型指针,用于访问特定类型中的数据
#define le2myType(le, member) \
to_struct((le), struct myType, member)
主要需要使用到以上三个宏。
o
f
f
s
e
t
o
f
offsetof
offsetof用于求出
t
y
p
e
type
type类型数据结构中成员
m
e
m
b
e
r
member
member的偏移量(相对于首地址的偏移量),主要用到编译器的特性,将零地址强制转化成
t
y
p
e
type
type类型。
o
f
f
s
e
t
o
f
=
t
y
p
e
.
m
e
m
b
e
r
−
t
y
p
e
b
a
s
e
=
t
y
p
e
.
m
e
m
b
e
r
−
0
=
t
y
p
e
.
m
e
m
b
e
r
offsetof=type.member-typebase=type.member-0=type.member
offsetof=type.member−typebase=type.member−0=type.member
t
o
_
s
t
r
u
c
t
to\_struct
to_struct用于将
p
t
r
ptr
ptr转化为
t
y
p
e
type
type类型指针。
通过
o
f
f
s
e
t
o
f
offsetof
offsetof宏可以计算出结构体中一个成员距离首地址的距离
d
d
d
反过来,知道这个结构体成员的地址
p
t
r
ptr
ptr以及距离
d
d
d,可以计算出首地址。
t
y
p
e
b
a
s
e
=
t
y
p
e
.
m
e
m
b
e
r
−
o
f
f
s
e
t
o
f
=
p
t
r
−
d
typebase=type.member-offsetof=ptr-d
typebase=type.member−offsetof=ptr−d
l
e
2
m
y
T
y
p
e
le2myType
le2myType为
t
o
_
s
t
r
u
c
t
to\_struct
to_struct的简单封装。需要根据特定数据类型来确定。
其实如果将 l i s t _ e n t r y _ t list\_entry\_t list_entry_t类型成员作为自定义数据类型结构体中的第一个成员,那么直接强制类型转化即可,因为此时 o f f s e t o f offsetof offsetof一定为 0 0 0
下面结合一个实例来学习这种双向循环链表的使用。
#include <iostream>
using namespace std;
#include "typelist.h"
// 普通双向循环链表结构
struct myOrdinaryType {
int data;
struct myOrdinaryType *prev, *next;
};
// 通用双向循环链表结构
struct myType{
int data;
list_entry_t list_entry;
};
// 宏定义,将list_entry_t类型指针转化为特定类型指针,用于访问特定类型中的数据
#define le2myType(le, member) \
to_struct((le), struct myType, member)
// 遍历链表
void printList(list_entry_t* le){
if(list_empty(le)){
return;
}
list_entry_t* it = le;
while((it=list_next(it)) != le){
// 利用le2myType宏,将list_entry_t类型指针转化为struct myType类型指针
struct myType *p = le2myType(it, list_entry);
std::cout << p->data << " ";
}
std::cout << std::endl;
}
int main()
{
// 定义一个头结点以及三个数据节点
// 头节点中的数据并没有意义,并不会被使用,这里定义为-1无任何意义
struct myType head; head.data = -1;
struct myType node1; node1.data = 1;
struct myType node2; node2.data = 2;
struct myType node3; node3.data = 3;
// 初始化头结点
list_init(&head.list_entry);
// 将三个数据节点加入链表中
list_add(&head.list_entry, &node1.list_entry);
list_add(&node1.list_entry, &node2.list_entry);
list_add(&node2.list_entry, &node3.list_entry);
list_entry_t * le = &head.list_entry; //le是空闲块链表头指针
// 遍历链表,输出结果为: 1 2 3
printList(le);
// 删除第三个节点
list_del(&node3.list_entry);
printList(le); // 输出结果为: 1 2
// 删除第二个节点
list_del(&node2.list_entry);
printList(le); // 输出结果为: 1
// 删除第一个节点
list_del(&node1.list_entry);
printList(le); // 输出结果为:
return 0;
}
内核双向循环链表完整定义
#ifndef TYPELIST_H
#define TYPELIST_H
/* Return the offset of 'member' relative to the beginning of a struct type */
#define offsetof(type, member) \
((size_t)(&((type *)0)->member))
/* *
* to_struct - get the struct from a ptr
* @ptr: a struct pointer of member
* @type: the type of the struct this is embedded in
* @member: the name of the member within the struct
* */
#define to_struct(ptr, type, member) \
((type *)((char *)(ptr) - offsetof(type, member)))
struct list_entry_t {
struct list_entry_t *prev, *next;
};
static inline void list_init(list_entry_t* elm) __attribute__((always_inline));
static inline void list_add(list_entry_t* listelm, list_entry_t* elm) __attribute__((always_inline));
static inline void list_add_before(list_entry_t* listelm, list_entry_t* elm) __attribute__((always_inline));
static inline void list_add_after(list_entry_t* listelm, list_entry_t* elm) __attribute__((always_inline));
static inline void list_del(list_entry_t* listelm) __attribute__((always_inline));
static inline void list_del_init(list_entry_t* listelm) __attribute__((always_inline));
static inline bool list_empty(list_entry_t* list) __attribute__((always_inline));
static inline list_entry_t *list_next(list_entry_t* listelm) __attribute__((always_inline));
static inline list_entry_t *list_prev(list_entry_t* listelm) __attribute__((always_inline));
static inline void __list_add(list_entry_t* elm, list_entry_t* prev, list_entry_t* next) __attribute__((always_inline));
static inline void __list_del(list_entry_t* prev, list_entry_t* next) __attribute__((always_inline));
/* *
* list_init - initialize a new entry
* @elm: new entry to be initialized
* */
static inline void list_init(list_entry_t* elm) {
elm->prev = elm->next = elm;
}
/* *
* list_add - add a new entry
* @listelm: list head to add after
* @elm: new entry to be added
*
* Insert the new element @elm *after* the element @listelm which
* is already in the list.
* */
static inline void list_add(list_entry_t* listelm, list_entry_t* elm) {
__list_add(elm, listelm, listelm->next);
}
/* *
* list_add_before - add a new entry
* @listelm: list head to add before
* @elm: new entry to be added
*
* Insert the new element @elm *before* the element @listelm which
* is already in the list.
* */
static inline void list_add_before(list_entry_t* listelm, list_entry_t* elm) {
__list_add(elm, listelm->prev, listelm);
}
/* *
* list_add_after - add a new entry
* @listelm: list head to add after
* @elm: new entry to be added
*
* Insert the new element @elm *after* the element @listelm which
* is already in the list.
* */
static inline void list_add_after(list_entry_t* listelm, list_entry_t* elm) {
__list_add(elm, listelm, listelm->next);
}
/* *
* list_del - deletes entry from list
* @listelm: the element to delete from the list
*
* Note: list_empty() on @listelm does not return true after this, the entry is
* in an undefined state.
* */
static inline void list_del(list_entry_t* listelm) {
__list_del(listelm->prev, listelm->next);
}
/* *
* list_del_init - deletes entry from list and reinitialize it.
* @listelm: the element to delete from the list.
*
* Note: list_empty() on @listelm returns true after this.
* */
static inline void list_del_init(list_entry_t* listelm) {
list_del(listelm);
list_init(listelm);
}
/* *
* list_empty - tests whether a list is empty
* @list: the list to test.
* */
static inline bool list_empty(list_entry_t* list) {
return list->next == list;
}
/* *
* list_next - get the next entry
* @listelm: the list head
**/
static inline list_entry_t* list_next(list_entry_t* listelm) {
return listelm->next;
}
/* *
* list_prev - get the previous entry
* @listelm: the list head
**/
static inline list_entry_t* list_prev(list_entry_t* listelm) {
return listelm->prev;
}
/* *
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
* */
static inline void __list_add(list_entry_t* elm, list_entry_t* prev, list_entry_t* next) {
prev->next = next->prev = elm;
elm->next = next;
elm->prev = prev;
}
/* *
* Delete a list entry by making the prev/next entries point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
* */
static inline void __list_del(list_entry_t* prev, list_entry_t* next) {
prev->next = next;
next->prev = prev;
}
#endif // TYPELIST_H