一、自己学习链表

数组的缺点:(1)数据类型一致;(2)数组的长度事先定好,不能灵活更改。

从而引入了链表来解决数组的这些缺点:(1)结构体解决多数据类型(2)链表的组合使得链表的长度可以灵活设置。

基本概念:

头结点:

这个节点是为了便于管理链表的节点,这个节点并不保存数据;虽然和其他节点一样,但是这个头结点是指向首节点的节点。

首节点:

第一个保存有效数据的节。

尾节点:

最后一个保存有效数据的节点

头指针:

头指针是指向头节点的指针。


单链表:

链表节点的数据结构定义:

typedef struct Node
{
int data;
struct Node *PNEXT;
}NODE,*PNODE;

单链表的代码:


typedef struct Node
{
int data;
struct Node *PNEXT;
}NODE,*PNODE; // 创建链表的节点
PNODE create_node(int data)
{
// 创建结点
PNODE p = (PNODE)malloc(sizeof(NODE));
if (NULL == p)
{
printf("malloc error\n");
exit(-);
}
memset(p, , sizeof(NODE)); p->data = data;
p->PNEXT = NULL; return p;
} // 链表的初始化
PNODE create_list()
{ int iLen_list = ;
int iData_list = ; // 创建头结点
PNODE pHead = NULL;
pHead = (PNODE)malloc(sizeof(NODE));
if (NULL == pHead)
{
printf("malloc pHead error\n");
exit(-);
}
pHead->data = NULL;
pHead->PNEXT = NULL; // 定义一个尾指针
PNODE pTail = pHead; printf("输出链表的长度,n = \n");
scanf("%d", &iLen_list); // 初始化创建节点的数据
for (size_t i = ; i < iLen_list; i++)
{
PNODE pNew = NULL;
printf("输出链表第 %d 个的值", i + );
scanf("%d", &iData_list); // 创建节点
pNew = create_node(iData_list); // 保证尾指针是一直指向最后一个节点
pTail->PNEXT = pNew;
pTail = pNew;
} return pHead;
} // 判断链表是否为空
bool is_list_empty(PNODE pHead)
{
if (pHead->PNEXT == nullptr)
return true;
else
return false;
} // 链表的长度
int len_list(PNODE pHead)
{
PNODE pNew = pHead;
int i = ; while ( pNew->PNEXT != nullptr )
{
i++;
pNew = pNew->PNEXT;
}
return i;
} // 遍历链表的所有节点
void traver_all_list(PNODE pHead)
{ if (is_list_empty(pHead))
{
printf("空链表\n");
exit(-);
} PNODE pNew = pHead;
while (pNew->PNEXT != nullptr )
{
pNew = pNew->PNEXT;
printf("%d\n", pNew->data);
}
} // 链表的尾部添加数据
bool list_append_tail(PNODE pHead,int data)
{ int i = NULL;
if (is_list_empty(pHead))
{
printf("空链表\n");
return false;
} PNODE pNew = pHead;
while ((pNew->PNEXT != nullptr) )
{ // pNew 最后指向最后一个节点
pNew = pNew->PNEXT;
} PNODE pNewOne = create_node(data);
pNew->PNEXT = pNewOne; pNewOne->data = data;
pNewOne->PNEXT = nullptr; return true;
} // 链表的尾部插入数据
bool list_insert_tail(PNODE pHead, int data)
{ int i = NULL;
if (is_list_empty(pHead))
{
printf("空链表\n");
return false;
} PNODE pNew = pHead;
while ( (pNew->PNEXT != nullptr) && ( i<len_list(pHead) - ) )
{ // pNew 最后指向最后一个节点
pNew = pNew->PNEXT;
i++;
} PNODE pNewOne = create_node(data);
pNewOne->PNEXT = pNew->PNEXT;
pNewOne->data = data; pNew->PNEXT = pNewOne; return true;
} // 链表的头也就是添加一个新的链表的首节点
bool list_insert_head(PNODE pHead, int data)
{
PNODE pNew = pHead; // 创建新的首节点,并使之节点指向旧的首节点
PNODE pNewOne = create_node(data);
pNewOne->data = data;
pNewOne->PNEXT = pNew->PNEXT; // 头结点指向首节点
pNew->PNEXT = pNewOne;
return true;
} // 插入链表 N 位置
bool list_N_insert(PNODE pHead,int n, int data)
{ PNODE pNew = pHead;
int i = ; // 空的链表就不要插入了
if (is_list_empty(pHead))
{
printf("空链表\n");
return false;
} // 插入首节点
if (n >len_list(pHead) + )
{
cout << "添加的位置大于链表的长度" << endl;
return false;
}
else if ( == n )
{
return list_insert_head(pHead,data);
}
else if (n == len_list(pHead))
{
// 插入尾节点
return list_insert_tail(pHead,data);
}
else
{
while ( (pNew->PNEXT != nullptr) && (i<n-))
{
// 在 N 的位置插入,则必须使得 pNew 指向 n-1 的位置
pNew = pNew->PNEXT;
i++;
} PNODE pNewOne = create_node(data);
pNewOne->data = data;
pNewOne->PNEXT = pNew->PNEXT;
pNew->PNEXT = pNewOne; return true;
} } // 删除链表的首节点
bool delete_list_heap(PNODE pHead)
{
if (is_list_empty(pHead))
{ // 空的链表的话,就没有什么好删除的
printf("空链表,不需要删除\n");
return false;
} // 指向首节点
PNODE pNew = pHead->PNEXT;
// 头结点指向第二个节点
pHead->PNEXT = pNew->PNEXT;
cout << "删除节点的数值是:" << pNew->data << endl; free (pNew);
pNew = nullptr; return true;
} // 删除链表的尾节点
bool delete_list_tail(PNODE pHead)
{
int i = NULL; if (is_list_empty(pHead))
{
printf("空链表\n");
return false;
} PNODE pNew = pHead;
while ((pNew->PNEXT != nullptr) && (i < len_list(pHead))-)
{ // 使得 pNew 指向尾节点的倒数一个节点
pNew = pNew->PNEXT;
i++;
} PNODE pDelOne = pNew->PNEXT;
cout << "删除节点的数值是:" << pDelOne->data << endl; pNew->PNEXT = nullptr;
free(pDelOne);
pDelOne = nullptr; return true;
} // 删除链表 N 位置
bool delete_N_list(PNODE pHead, int n)
{
PNODE pNew = pHead;
int i = ; // 空的链表就不要插入了
if (is_list_empty(pHead))
{
printf("空链表\n");
return false;
} if (n > len_list(pHead))
{
cout << "删除的位置大于链表的长度" << endl;
return false;
}
else if ( == n )
{// 删除首节点
return delete_list_heap(pHead);
}
else if (n == len_list(pHead))
{ // 删除尾节点
return delete_list_tail(pHead);
}
else
{ // 删除除了首节点尾节点以外的节点,pNew 指向删除节点前面的那个节点
while ((pNew->PNEXT != nullptr) && (i<n-) )
{
pNew = pNew->PNEXT;
i++;
} PNODE pDelOne = pNew->PNEXT;
pNew->PNEXT = pDelOne->PNEXT;
cout << "删除节点的数值是:" << pDelOne->data << endl;
free(pDelOne);
pDelOne = nullptr; return true;
}
} // 链表的排序
bool sort_list(PNODE pHead)
{
if (is_list_empty(pHead))
{
printf("空链表\n");
return false;
} int n = len_list(pHead); PNODE pp = nullptr;
PNODE qq = nullptr;
int i, j;
int Temp; for (pp = pHead->PNEXT, i = ; i < n-; i++, pp = pp->PNEXT)
{
for (qq = pp->PNEXT, j = i+; j < n; j++,qq = qq->PNEXT)
{
if ( pp->data > qq->data )
{
Temp = pp->data;
pp->data = qq->data;
qq->data = Temp;
}
}
} return true;
} int main(int argc, char *argv[])
{ PNODE pHead = NULL;
int iLen_lis = NULL; // 创建链表已经初始化
pHead = create_list(); // 链表的遍历
traver_all_list(pHead); // 计算链表长度
iLen_lis = len_list(pHead);
cout << "链表的长度是:" << iLen_lis << endl; // 链表尾部添加数据
if (list_append_tail(pHead, ))
{
cout << "链表的尾部添加数据成功" << endl; iLen_lis = len_list(pHead);
cout << "链表的长度是:" << iLen_lis << endl;
traver_all_list(pHead);
} // 链表头部添加数据
cout << endl;
if (list_insert_head(pHead, ))
{
cout << "链表的首节点添加数据成功" << endl; iLen_lis = len_list(pHead);
cout << "链表的长度是:" << iLen_lis << endl;
traver_all_list(pHead);
} // 指定位置插入数据
cout << endl;
if (list_N_insert(pHead,,))
{
cout << "插入成功" << endl;
iLen_lis = len_list(pHead);
cout << "链表的长度是:" << iLen_lis << endl;
traver_all_list(pHead);
} // 指定位置删除数据
cout << endl;
if (delete_N_list(pHead, ))
{
cout << "删除成功" << endl;
iLen_lis = len_list(pHead);
cout << "链表的长度是:" << iLen_lis << endl;
traver_all_list(pHead);
} // 链表的排序
cout << endl;
if ( sort_list(pHead) )
{
cout << "排序成功" << endl;
traver_all_list(pHead);
} while ();
}

经过自己的实测是正确的:

输出链表的长度,n =

输出链表第  个的值1
输出链表第 个的值2
输出链表第 个的值3 链表的长度是:
链表的尾部添加数据成功
链表的长度是: 链表的首节点添加数据成功
链表的长度是: 插入成功
链表的长度是: 删除节点的数值是:
删除成功
链表的长度是: 排序成功

补充:单链表的逆序

bool reverse_list(PNODE pHead)
{
if (is_list_empty(pHead))
{
printf("空链表\n");
return false;
} PNODE Temp0 = pHead;
PNODE Temp1 = pHead;
PNODE Temp3 = pHead->PNEXT;
PNODE Temp2 = nullptr;
int i = ;
while (Temp3->PNEXT != nullptr)
{
Temp2 = Temp3;
Temp3 = Temp3->PNEXT;
if ( == i)
{
Temp2->PNEXT = nullptr;
}
else
{
Temp2->PNEXT = Temp1;
}
i++;
Temp1 = Temp2;
}
Temp3->PNEXT = Temp2;
Temp0->PNEXT = Temp3;
return true;
}

传入了头结点的指针,

首先 T2 接替 T3,T2指向了下一个节点,而 T1 接替 T2,就这样一部一部,使之 T2 永远指向 T1,当 T3 结束的时候,T3 是没有指向 T2 的,所以退出循环就执行Temp3->PNEXT = Temp2;,而头结点 T0->PNEXT

= T3.

双链表:

因为单链表的操作的不便(一旦指针指向一个节点,就无法返回来,必须重新进行循环),所以就引入了双向链表。

双向链表的数据定义:

typedef struct Node
{
int data;
struct Node * PPREV;
struct Node * PNEXT;
}NODE, *PNODE;

因为是双向链表,所以就定义了两个指向节点的指针,prev 往前指,next 指向后面的节点。

特殊的是:头结点的 prev 是指向尾节点(最后一个节点),而尾节点的 next 是指向头结点的。

代码:

#define DEBUG
#ifdef DEBUG
#define DBG(fmt, args,...) printf(fmt, ##args)
#else
#define DBG(fmt, args...) do {} while (0)
#endif
typedef struct Node
{
int data;
struct Node * PPREV;
struct Node * PNEXT;
}NODE, *PNODE;
// 创建单个节点
PNODE create_node(int data)
{
PNODE pNew = nullptr;
pNew = (PNODE)malloc(sizeof(NODE));
if ( nullptr == pNew)
{
cout << " malloc error" << endl;
exit(-);
}
pNew->PNEXT = nullptr;
pNew->PPREV = nullptr;
pNew->data = data;
return pNew;
}
// 链表的初始化
PNODE create_list()
{
int i = ;
int iLenList = NULL;
int iDataList = NULL;
PNODE pHead = nullptr;
PNODE pTail = nullptr;
pHead = (PNODE)malloc(sizeof(NODE));
if ( nullptr == pHead )
{
cout << " malloc error" << endl;
exit(-);
}
pHead->data = NULL;
pHead->PNEXT = pHead->PPREV = nullptr;
pTail = pHead; printf("输入链表的长度 n = ");
scanf("%d", &iLenList);
for ( i = ; i < iLenList; i++)
{
printf("输入创建 第 %d 节点的数据\n", i + );
scanf("%d", &iDataList);
PNODE pNew = create_node( iDataList );
pTail->PNEXT = pNew;
pHead->PPREV = pNew;
pNew->PPREV = pTail;
pNew->PNEXT = pHead;
pTail = pNew;
}
return pHead;
}
bool list_is_empty(PNODE pHead)
{
PNODE pNew = pHead;
if ( pNew->PNEXT == nullptr )
{
return true;
}
else
{
return false;
}
}
// 双向链表的遍历
bool traver_list(PNODE pHead)
{
if ( list_is_empty(pHead))
{
cout << "空链表" << endl;
return false;
}
PNODE pNew = pHead, pHeadNew = pHead;
while (pNew->PNEXT != pHeadNew)
{
pNew = pNew->PNEXT;
cout << pNew->data << endl;
}
return true;
}
// 计算链表的长度
int len_list(PNODE pHead)
{
PNODE pHeadNew = pHead;
int i = NULL;
PNODE pNew = pHead;
while ( pNew->PNEXT != pHeadNew )
{
pNew = pNew->PNEXT;
i++;
}
return i;
}
// 链表的尾部添加数据
bool list_tail_append_data(PNODE pHead, int data)
{
PNODE pHeadNew = pHead;
PNODE pTail = pHead;
DBG("%d \n", __LINE__);
int n = len_list(pHead);
int i = ;
// 抱枕 pTail 指向最后一个节点
while ( i < (n) ) // 从头结点到尾节点需要移动 n(链表长度)次数
{
pTail = pTail->PNEXT;
i++;
}
// 创建新的节点
PNODE pNewOne = (PNODE)malloc(sizeof(NODE));
if ( nullptr == pNewOne )
{
cout << "malloc error" << endl;
return false;
}
pNewOne->data = data;
DBG("%d \n", __LINE__);
pTail->PNEXT = pNewOne;
pNewOne->PPREV = pTail;
pNewOne->PNEXT = pHeadNew;
pHeadNew->PPREV = pNewOne;
DBG("%d \n", __LINE__);
return true;
}
// 链表的头部添加数据
bool list_heap_insert_data(PNODE pHead, int data)
{
if ( list_is_empty(pHead))
{
printf("空链表 \n");
return false;
}
PNODE pHeadNew = pHead;
PNODE pNewOne = (PNODE)malloc(sizeof(NODE));
pNewOne->data = data;
pNewOne->PNEXT = pHeadNew->PNEXT;
pHeadNew->PNEXT->PPREV = pNewOne;
pHeadNew->PNEXT = pNewOne;
return true;
}
bool list_tail_insert(PNODE pHead,int data)
{
PNODE pHeadNew = pHead;
PNODE pNew = pHead;
int i = ;
int n = len_list(pHeadNew);
while (i<(n-))
{ // pNew 指向尾节点的前面一个节点,
i++;
pNew = pNew->PNEXT;
}
PNODE pNewOne = (PNODE)malloc(sizeof(NODE));
pNewOne->data = data;
pNewOne->PNEXT = pNew->PNEXT;
pNew->PNEXT->PPREV = pNewOne;
pNewOne->PNEXT = pNew;
pNew->PNEXT = pNewOne;
return true;
}
// 指定位置插入数据
bool list_N_insert(PNODE pHead, int iPos, int data)
{
PNODE pHeadNew = pHead;
PNODE pNew = pHead;
if (list_is_empty(pHead))
{
printf("空链表 \n");
return false;
}
int i = NULL;
int n = len_list(pHeadNew);
if ( iPos > n)
{
printf("出入位置大于链表的长度,不能执行插入\n");
return false;
}
else if ( n == )
{// 在首结点插入数据
return list_heap_insert_data(pHeadNew, data);
}
else if ( n == iPos)
{// 在尾节点插入数据
return list_tail_insert(pHeadNew,data);
}
else
{
// 在除了尾节点首节点插入数据
while (i < (iPos - ))
{// 使得 pNew 指向删除节点的前面一个节点
pNew = pNew->PNEXT;
i++;
}
PNODE pNewOne = create_node(data);
pNewOne->PNEXT = pNew->PNEXT;
pNew->PNEXT->PPREV = pNewOne;
pNew->PNEXT = pNewOne;
pNewOne->PPREV = pNew;
return true;
}
}
// 删除链表首节点
bool delete_heap_list(PNODE pHead)
{
PNODE pHeadNew = pHead;
PNODE pNew;
int n = len_list(pHead);
if ( n == )
{
pNew = pHeadNew->PNEXT;
pHeadNew->PNEXT = pHeadNew;
pHeadNew->PNEXT = pHeadNew;
printf("删除数据是%d\n", pNew->data);
free(pNew);
pNew = nullptr;
return true;
}
else
{
pNew = pHeadNew->PNEXT;
pHeadNew->PNEXT = pHeadNew->PNEXT->PNEXT;
pHeadNew->PNEXT->PPREV = pHeadNew;
printf("删除数据是%d\n", pNew->data);
free(pNew);
pNew = nullptr;
return true;
}
}
// 删除链表的尾节点
bool delete_tail_list(PNODE pHead)
{
PNODE pHeadNew = pHead;
PNODE pNew = pHead;
PNODE pNewOne = nullptr;
int n = len_list(pHead);
int i = NULL;
while (i < (n - ))
{// pNew 指向删除节点的前面一个节点
pNew = pNew->PNEXT;
i++;
}
pNewOne = pNew->PNEXT;
printf("删除数据是%d\n", pNewOne->data);
pNew->PNEXT = pHeadNew;
pHeadNew->PPREV = pNew;
free(pNewOne);
pNewOne = nullptr; return true;
}
// 删除链表的任意的位置
bool delete_N_list(PNODE pHead,int iPos)
{
if (list_is_empty(pHead))
{
printf("空链表 \n");
return false;
}
PNODE pNew = pHead;
PNODE pNewOne = pHead;
int i = NULL;
int n = len_list(pHead);
if (iPos > n)
{
printf("删除位置大于链表的长度,不能执行删除\n");
return false;
}
else if ( iPos == )
{
return delete_heap_list(pHead);
}
else if (iPos == n)
{
return delete_tail_list(pHead);
}
else
{
while (i<(iPos - ))
{
pNew = pNew->PNEXT;
i++;
}
pNewOne = pNew->PNEXT;
pNew->PNEXT = pNewOne->PNEXT;
pNewOne->PNEXT->PPREV = pNew;
printf("删除数据是%d\n", pNewOne->data);
free(pNewOne);
pNewOne = nullptr;
return true;
}
}
// 对链表进行排序
bool list_sort(PNODE pHead)
{
if (list_is_empty(pHead))
{
printf("空链表,排序失败\n");
return false;
}
int n = len_list(pHead);
PNODE ppNew = nullptr;
PNODE qqNew = nullptr;
int i = , j = ;
int TempDat = NULL;
for (ppNew = pHead->PNEXT, i = ; i < (n - );i++,ppNew = ppNew->PNEXT)
{
for (qqNew = ppNew->PNEXT, j = i+; j < n;j++,qqNew=qqNew->PNEXT)
{
if ( ppNew->data > qqNew->data)
{
TempDat = ppNew->data;
ppNew->data = qqNew->data;
qqNew->data = TempDat;
}
}
}
return true;
}
int main(int argc, char **argv)
{
int iLenList = NULL;
PNODE pHead = nullptr;
// 双向链表的初始化
pHead = create_list();
if (traver_list(pHead))
{
cout << "遍历成功" << endl;
}
iLenList = len_list(pHead);
printf("链表长度等于 %d\n", iLenList);
// 链表的尾部添加数据
if (list_tail_append_data(pHead, ))
{
list_tail_append_data(pHead, );
printf("尾部添加数据成功\n");
traver_list(pHead);
}
if (list_heap_insert_data(pHead, ))
{
list_heap_insert_data(pHead, );
printf("头部添加数据成功\n");
traver_list(pHead);
}
// 链表的任意位置添加数据
if (list_N_insert(pHead,,))
{
printf("位置3添加数据成功\n");
traver_list(pHead);
}
// 任意位置删除数据
if (delete_N_list(pHead, ))
{
printf("位置8删除数据成功\n");
traver_list(pHead);
}
// 排序
if ( list_sort(pHead) )
{
printf("排序成功\n");
traver_list(pHead);
}
while ();
}

双链表和单链表的操作其实很多的类似,参考者编写代码,还是比较简单的。参照了单链表,也是设置了头结点用于帮助设计双向链表,然后还有首尾节点,这些才是正真保存数据的开始的节点和结束的节点。

二、内核链表的学习

对于链表的操作自己编写的话过于麻烦,而Linux内核提供了对应的API,可以直接调用方便使用:D:\source insight\linux2.6.35.7\android-kernel-samsung-dev\include\linux 的 list.h。

0、链表节点的指针

struct list_head
{
struct list_head *next, *prev;
};

链表结构体的指针有两个:next 指向下一个节点,prev 指向上一个节点。也就是说内核链表具备了双向链表的功能,而且链表只是纯链表,并不具备数据类型,所以具备非常大的通用性,这样可以自己根据自己的实际的需求去设计。

1、链表头结点的初始化

(1)定义且初始化

#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)

链表头结点定义的时候且完成初始化,将上面的宏进行进行展开:

#define LIST_HEAD(name) \
struct list_head name = { &(name), &(name) }

是将链表的头结点的两个指针分别都指向了自己,从而完成链表头结点的初始化。

(2)先定义后完成初始化

static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}

对于一个已经完成链表头节点初始化,那么对这个链表头则是调用这个函数来完成初始化。这里函数实现链表头节点的初始化与上面宏完成初始化是一样的,差别无非是定义且初始化,一个是先定义头结点后完成初始化。

2、链表节点的添加

static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}

对于链表节点的添加,这里需要知道,默认的都是进行尾添加,也就是在节点的后面进行添加。

注意:

学习发现,内核的双向链表是其实也是借助了头结点了。

2.1、链表的头结点进行添加

static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}

new 指向全新的节点,而 head 是指向头节点,而head->next 是指头首节点的下一个节点,也就是第二个节点。所以插入的节点 new 是在第一个和第二个节点直接之间完成节点的插入。

2.2、链表尾部节点的添加

static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}

new 指向新的节点,而 head->prev 指向链表的尾节点,head 指向链表的首节点。所以 new 是被插入在首节点和尾节点之间。

3、链表删除一个节点

static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}

static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}

entry 是指向删除节点的指针。 entry->prev 是指向删除节点的前面一个节点,而 entrt->next 指向删除节点的下一个节点。而将删除节点的 prev 和 next 分别设置为 position,对它的定义为:

/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA)
#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)

理解内核的注释, position 不是一个空指针,但是会引起页的错误。

4、节点的替换

static inline void list_replace(struct list_head *old,
struct list_head *new)
{
new->next = old->next;
new->next->prev = new;
new->prev = old->prev;
new->prev->next = new;
}

代码还是比较的简单,完成新老节点替换,使得新的节点的指针指向老节点的指向。

static inline void list_replace_init(struct list_head *old,
struct list_head *new)
{
list_replace(old, new);
INIT_LIST_HEAD(old);
}

完成新老节点的替换,然后将老节点进行初始化。

5、节点的移动

/**
* list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
*/
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del(list->prev, list->next);
list_add(list, head);
}

将 list 指向的节点从 head 为开始的头结点的链表删除之后,又移动到这个链表的首节点。也就是在头结点的后面。

/**
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del(list->prev, list->next);
list_add_tail(list, head);
}

将节点 list 从链表 中删除,并将节点 list 添加到链表(以 head 为头结点)的尾部。

6、链表的判断

/**
* list_is_last - tests whether @list is the last entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_last(const struct list_head *list,
const struct list_head *head)
{
return list->next == head;
}

判断节点 list 是不是链表的最后一个节点。

list 是判断的链表的节点。

head:是双向量表的头结点。

代码和简单,就是判断 list 节点的下一个节点是不是 head(头结点)。

/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}

判断链表是否为空:

其实就是判断自己的下一个节点是不是指向了自己,因为只有空的链表,也就是只有一个头结点的话,才会自己指向自己。

/**
* list_empty_careful - tests whether a list is empty and not being modified
* @head: the list to test
*
* Description:
* tests whether a list is empty _and_ checks that no other CPU might be
* in the process of modifying either member (next or prev)
*
* NOTE: using list_empty_careful() without synchronization
* can only be safe if the only activity that can happen
* to the list entry is list_del_init(). Eg. it cannot be used
* if another CPU could re-list_add() it.
*/
static inline int list_empty_careful(const struct list_head *head)
{
struct list_head *next = head->next;
return (next == head) && (next == head->prev);
}

判断链表是否为空,这次的判断是通过头结点的前驱和后驱是不是指向同一个节点。按照字面上面的解释,是怕在判断的时候被CPU 的其他的线程修改了数据,因此这种判断的方法是比较的正确的。

7、链表的首节点放到尾节

/**
* list_rotate_left - rotate the list to the left
* @head: the head of the list
*/
static inline void list_rotate_left(struct list_head *head)
{
struct list_head *first;
if (!list_empty(head)) {
first = head->next;
list_move_tail(first, head);
}
}

显示判断链表不为空的时候,让链表的首节点放到尾节点。

8、判断链表是否只有一个的节点(首节点)

/**
* list_is_singular - tests whether a list has just one entry.
* @head: the list to test.
*/
static inline int list_is_singular(const struct list_head *head)
{
return !list_empty(head) && (head->next == head->prev);
}

当链表只有一个节点的时候,也就是存在一个头结点和首节点,所以这个时候头结点的头指针和尾指针都是指向首节点的。

9、链表的遍历

#define list_entry(ptr, type, member) \
container_of(ptr, type, member)

type :结构体的类型

member:结构体的成员变量

ptr:返回结构体的起始的地址,我估计这个点应该是链表的头结点的地址


#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)

返回结构体的初始地址的下一个节点的初始地址(我估计应该是链表的首节点的地址)。

上面的介绍,已经可以基本对内核有了基本的认识。

三、内核链表的使用

内核链表提供的都是纯链表,而对于链表的数据类型是通过自己灵活指定的,是将链表的结构整个内嵌到链表的结构体里面。

struct
{
int goal;
int id;
struct list_head head;
}

goal和 id 是数据,而 head 在是链表的指针的结构;而自己可以灵活去设置自己的数据区域。

因为单链表的操作的不便(一旦指针指向一个节点,就无法返回来,必须重新进行循环),所以就引入了双向链表。

对于链表的操作自己编写的话过于麻烦,而Linux内核提供了对应的API,可以直接调用方便使用:D:\source insight\linux2.6.35.7\android-kernel-samsung-dev\include\linux 的 list.h。

0、链表节点的指针

struct list_head

{

struct list_head *next, *prev;

};

链表结构体的指针有两个:next 指向下一个节点,prev 指向上一个节点。

1、链表头结点的初始化

(1)定义且初始化

#define LIST_HEAD_INIT(name) { &(name), &(name) }

 

#define LIST_HEAD(name) \

struct list_head name = LIST_HEAD_INIT(name)

链表头结点定义的时候且完成初始化,将上面的宏进行进行展开:

#define LIST_HEAD(name) \

struct list_head name = { &(name), &(name) }

是将链表的头结点的两个指针分别都指向了自己,从而完成链表头结点的初始化。

(2)先定义后完成初始化

static inline void INIT_LIST_HEAD(struct list_head *list)

{

list->next = list;

list->prev = list;

}

对于一个已经完成链表头节点初始化,那么对这个链表头则是调用这个函数来完成初始化。这里函数实现链表头节点的初始化与上面宏完成初始化是一样的,差别无非是定义且初始化,一个是先定义头结点后完成初始化。

Linux内核中链表的学习的更多相关文章

  1. linux内核中链表代码分析---list.h头文件分析(一)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637596.html linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17 ...

  2. linux内核中链表代码分析---list.h头文件分析(二)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637598.html linux内核中链表代码分析---list.h头文件分析(二) 16年2月28日16 ...

  3. Linux内核中链表实现

    关于双链表实现,一般教科书上定义一个双向链表节点的方法如下: struct list_node{ stuct list_node *pre; stuct list_node *next; ElemTy ...

  4. Linux内核中链表的实现与应用【转】

    转自:http://blog.chinaunix.net/uid-27037833-id-3237153.html 链表(循环双向链表)是Linux内核中最简单.最常用的一种数据结构.         ...

  5. Linux内核中的机制学习总结

    一.驱动中的poll机制 1.简介:select()和poll()系统调用的本质一样,前者在 BSD UNIX 中引入的,后者在 System V 中引入的. 应用程序使用 select() 或 po ...

  6. Linux内核中的list用法和实现分析

    这些天在思考知识体系的完整性,发现总是对消息队列的实现不满意,索性看看内核里面的链表实现形式,这篇文章就当做是学习的i笔记吧.. 内核代码中有很多的地方使用了list,而这个list的用法又跟我们平时 ...

  7. linux内核中的C语言常规算法(前提:你的编译器要支持typeof和type)

    学过C语言的伙伴都知道,曾经比较两个数,输出最大或最小的一个,或者是比较三个数,输出最大或者最小的那个,又或是两个数交换,又或是绝对值等等,其实这些算法在linux内核中通通都有实现,以下的代码是我从 ...

  8. Linux内核中的算法和数据结构

    算法和数据结构纷繁复杂,但是对于Linux Kernel开发人员来说重点了解Linux内核中使用到的算法和数据结构很有必要. 在一个国外问答平台stackexchange.com的Theoretica ...

  9. LINUX内核分析第二周学习总结——操作系统是如何工作的

    LINUX内核分析第二周学习总结——操作系统是如何工作的 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course ...

随机推荐

  1. bzoj3609【HEOI2014】人人尽说江南好

    题意:http://www.lydsy.com/JudgeOnline/problem.php?id=3609 sol :博弈论  通过打表找规律,发现答案是%m循环的,且当m为偶数时取反  因为我太 ...

  2. linux删除大量文件

    1.建立一个空目录 mkdir -p /tmp/rsync_blank 2.确立需要清空的目标目录 /data/web/vip/htdocs/tuan 3.使用rsync同步删除(注意目录后面的“/” ...

  3. Kubectl管理工具

    1.常用指令如下 运行应用程序 [root@manager ~]# kubectl run hello-world --replicas=3 --labels="app=example&qu ...

  4. filesystem

    1 tmpfs 以下来源于维基百科: tmpfs是类Unix系统上暂存档存储空间的常见名称,通常以挂载文件系统方式实现,并将数据存储在易失性存储器而非永久存储设备中.和RAM disk的概念近似,但后 ...

  5. float 及 overflow 的理解

    1.CSS 盒子模型: 2.float 支持属性:left right none inherit(部分支持) (1)float 属性影响范围:对紧随其后的块儿级元素起作用. (2)清除浮动常用方法:在 ...

  6. 搭建git linux 服务器

    假设你已经有sudo权限的用户账号,下面,正式开始安装. 第一步,安装git: for Ubuntu或Debian $ sudo apt-get install git for Centos 更新一下 ...

  7. T-SQL还有个内置方法NULLIF()

    declare @cypic varchar if (NULLIF(@cypic, '') IS NOT NULL) begin print 1 end else begin print 2 end ...

  8. DB迁移:从SQL Server 2005到MySQL

    一.工具选择 依工作需要进行老产品升级,其中一项重要工作就是将SQL Server数据库改为MySQL数据库,故而在对<各种主流 SQLServer 迁移到 MySQL 工具对比>文章学习 ...

  9. JS读取/创建本地文件及目录文件夹的方法

    原文链接:http://www.cnblogs.com/ayan/archive/2013/04/22/3036072.html 注:以下操作只在IE下有效! Javascript是网页制作中离不开的 ...

  10. Docker(八):私有仓库

    有时候使用Docker Hub这样的公共仓库可能不太方便,用户可以创建一个本地仓库供私人使用. 在安装了Docker之后,可以获取官方的registry镜像来运行,docker-registry是官方 ...