--------------------list_empty_careful()--------- Static inline int list_empty_careful(const struct list_head *head) { struct list_head *next = head-next; return (next == head) (next == head-prev);
--------------------list_empty_careful()---------
Static inline int list_empty_careful(const struct list_head *head)
{
struct list_head *next = head->next;
return (next == head) && (next == head->prev);
}
分析:
1.只有一个头结点head,这时head指向这个头结点,head->next,head->prev指向head,即:head==head->next==head->prev,这时候list_empty_careful()函数返回1。
2. 有两个结点,head指向头结点,head->next,head->prev均指向后面那个结点, 即:head->next==head->prev,而head!=head->next,head!=head->prev. 所以函数将返回0
3.有三个及三个以上的结点,这是一般的情况,自己容易分析了。
注意:这里empty list是指只有一个空的头结点,而不是毫无任何结点。并且该头结点必须其head->next==head->prev==head
4.5链表合并
Linux还支持两个链表的拼接,提供给用户的具体函数是list_splice和list_splice_init:
--------------__list_splice()------------------
static inline void __list_splice(struct list_head *list, struct list_head *head)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
struct list_head *at = head->next;
first->prev = head;
head->next = first;
last->next = at;
at->prev = last;
}
将一个非空链表插入到另外一个链表中。不作链表是否为空的检查,由调用者默认保证。因为每个链表只有一个头节点,将空链表插入到另外一个链表中是没有意义的。但被插入的链表可以是空的。
--------------------list_splice()----------------
static inline void list_splice(struct list_head *list, struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head);
}
这种情况会丢弃list所指向的头结点,这是特意设计的,因为两个链表有两个头结点,要去掉一个头结点。只要list非空链,head无任何限制,该程序都可以实现链表合并。
-------------------list_splice_init()-----------------------------------
static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) 0
{
__list_splice(list, head);
INIT_LIST_HEAD(list);
}
}
将一个链表的有效信息合并到另外一个链表后,重新初始化空的链表头。
5、获取宿主对象指针
如果需要有某种数据结构的队列,就在这种数据结构定义内部放上一个list_head数据结构。例如,建立数据结构foo链表的方式是,在foo的定义中,嵌入了一个list_head成员list。这里foo就是所指的"宿主"。
typedef struct foo {
…
struct list_head list;
…
};
但是,如何通过list_head成员访问到宿主结构项呢?毕竟list_head不过是个连接件,而我们需要的是一个"特定"的数据结构链表。
先介绍几个基本宏:offsetof、typeof、containerof
-------\linux\stddef.h-----offsetof()-----------
#define __compiler_offsetof(a,b) __builtin_offsetof(a,b)
而__builtin_offsetof()宏就是在编译器中已经设计好了的函数,直接调用即可。
-------------------------------
#undef offsetof //取消先前的任何定义,可以保证下面的定义生效
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
一共4步
1. ( (TYPE *)0 ) 0地址强制 "转换" 为 TYPE结构的指针;
2. ((TYPE *)0)->MEMBER 访问结构中的数据成员;
3. &( ( (TYPE *)0 )->MEMBER)取出数据成员的地址;
4.(size_t)(&(((TYPE*)0)->MEMBER))结果转换类型.巧妙之处在于将0转换成(TYPE*),结构以内存空间首地址0作为起始地址,则成员地址自然为偏移地址;
例说明:
#include
typedef struct _test
{
char i;
int j;
char k;
}Test;
int main()
{
Test *p = 0;