最近在看内核里通知链, 发现其存储链表使用了二级指针; 难道用一级指针不能实现链表吗? 当然不是了,一级指针照样可以实现;为了比较两者的区别, 写了个简单的测试用例,用来比较两者的差别,记录下;
首先使用一级链表, 最直观的想法就是结构体里有个next指针, 当要插入元素时, 遍历要末尾节点, 然后添加新的元素:
#include <stdio.h>
#include <unistd.h>
struct my_daat {
int idx;
struct my_daat *next;
};
struct my_daat head_daat = {
.idx = 1,
};
//增加
void add_daat (struct my_daat *head, struct my_daat *d)
{
struct my_daat *tmp = head;
while(tmp->next != NULL) {
tmp = tmp->next;
}
tmp->next = d;
}
//删除
void del_daat (struct my_daat *head, struct my_daat *d)
{
struct my_daat *tmp = head;
while(tmp->next != NULL) {
if(tmp->next->idx == d->idx) {
if(tmp->next->next) {
tmp->next = tmp->next->next;
} else {
tmp->next = NULL;
}
break;
}
tmp = tmp->next;
}
}
//删除
void show_daat(struct my_daat *head)
{
struct my_daat *tmp = head;
printf("\n-----------------------b1---\n");
while(tmp->next){
printf("--->%s, %d, idx: %d\n", __func__, __LINE__, tmp->next->idx);
tmp = tmp->next;
}
printf("\n-----------------------e2---\n");
}
struct my_daat da = {
.idx = 2,
.next = NULL,
};
struct my_daat db = {
.idx = 3,
.next = NULL,
};
struct my_daat dc = {
.idx = 4,
.next = NULL,
};
int main(int argc, char** argv)
{
int *addr = 0;
add_daat(&head_daat, &da);
add_daat(&head_daat, &db);
add_daat(&head_daat, &dc);
show_daat(&head_daat);
del_daat(&head_daat, &db);
show_daat(&head_daat);
return 0;
}
运行结果:
book@ubuntu:/work/test$ ./app
-----------------------b1---
--->show_daat, 47, idx: 2
--->show_daat, 47, idx: 3
--->show_daat, 47, idx: 4
-----------------------e2---
-----------------------b1---
--->show_daat, 47, idx: 2
--->show_daat, 47, idx: 4
-----------------------e2---
上面的增加/删除/显示这3个函数都用到了局部变量’struct my_daat *tmp = head;', 可以使用二级指针替代它;
#include <stdio.h>
#include <unistd.h>
struct my_daat {
int idx;
struct my_daat *next;
};
struct my_daat head_daat = {
.idx = 1,
};
void add_daat (struct my_daat **head, struct my_daat *d)
{
while((*head) != NULL){
head = &((*head)->next);
}
*head = d;
}
void del_daat (struct my_daat **head, struct my_daat *d)
{
while((*head) != NULL){
if((*head)->idx == d->idx) {
if((*head)->next) {
(*head) = (*head)->next;
} else {
(*head) = NULL;
}
break;
}
head = &((*head)->next);
}
}
void show_daat(struct my_daat **head)
{
printf("\n-----------------------b1---\n");
while((*head) != NULL){
printf("--->%s, %d, idx: %d\n", __func__, __LINE__, (*head)->idx);
head = &((*head)->next);
}
printf("\n-----------------------e2---\n");
}
struct my_daat da = {
.idx = 2,
.next = NULL,
};
struct my_daat db = {
.idx = 3,
.next = NULL,
};
struct my_daat dc = {
.idx = 4,
.next = NULL,
};
struct my_daat *taa = &head_daat;
struct my_daat **tab = &taa;
int main(int argc, char** argv)
{
int *addr = 0;
add_daat(tab, &da);
add_daat(tab, &db);
add_daat(tab, &dc);
show_daat(tab);
del_daat(tab, &head_daat);
show_daat(tab);
return 0;
}
运行:
book@ubuntu:/work/test$ ./app
-----------------------b1---
--->show_daat, 42, idx: 1
--->show_daat, 42, idx: 2
--->show_daat, 42, idx: 3
--->show_daat, 42, idx: 4
-----------------------e2---
-----------------------b1---
--->show_daat, 42, idx: 2
--->show_daat, 42, idx: 3
--->show_daat, 42, idx: 4
-----------------------e2---
使用二级指针, 立马高大上很多; 内核里很多地方使用了这种方法;
存储字符设备:
//drivers/base/map.c,
int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
struct module *module, kobj_probe_t *probe,
int (*lock)(dev_t, void *), void *data)
{
...
for (i = 0, p -= n; i < n; i++, p++, index++) {
struct probe **s = &domain->probes[index % 255];
while (*s && (*s)->range < range)
s = &(*s)->next;
p->next = *s;
*s = p;
}
...
}
//通知链里添加节点:
static int notifier_chain_cond_register(struct notifier_block **nl,
struct notifier_block *n)
{
while ((*nl) != NULL) {
...
nl = &((*nl)->next);
}
n->next = *nl; //即: n->next = NULL;
rcu_assign_pointer(*nl, n); //相当于: *nl = n;
return 0;
}
发现都是同样的套路;