二级指针在链表里的使用

最近在看内核里通知链, 发现其存储链表使用了二级指针; 难道用一级指针不能实现链表吗? 当然不是了,一级指针照样可以实现;为了比较两者的区别, 写了个简单的测试用例,用来比较两者的差别,记录下;
首先使用一级链表, 最直观的想法就是结构体里有个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;
}

发现都是同样的套路;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值