提两个C语言里实现列表用到的技巧。会用的人可能会觉得很平常,但是第一次看到的时候还是觉得很厉害的:
- 两级指针:两级指针要怎么用呢?我们先来看一下大部分正常人实现列表的方法:
-
//定义链表节点struct struct node { data_type data; struct node *next, *prev; }*head = NULL; //然后插入一个节点就要这样: void insert(struct node *new) { new->next = head; if (head) head->prev = new; new->prev = NULL; head = new; } //删除就要这样 void remove(struct node *delete) { if (delete == head) { head = head->next; head->prev = NULL; } else { delete->prev->next = delete->next; if (delete->next) delete->next->prev = delete->prev; } free(delete); }
//这一次,我们把节点定义成这样 struct node { data_type data; struct node *next, **prev; }*head; //然后insert就变成了这样 void insert(struct node *new) { new->prev = &head; new->next = head; if(head) head->prev = &new->next; head = new; } //删除就成了这样 void remove(struct node *delete) { *delete->prev = delete->next; if (delete->next) delete->next->prev = delete->prev; free(delete); }
//同样的技巧还能用在单链表的删除节点上 //Before struct node *now, *prev; for(now = head; now != NULL; now = now->next){ if(now是要删的节点){ if(now == head) head = now->next; else prev->next = now->next; break; } prev = now; } //After struct list **nowp; for(nowp = &head; *nowp != NULL; nowp = &(*nowp)->next){ if((*lpp)是要删的节点){ *nowp = (*nowp)->next; break; } }
- container_of:正常人都知道,C不是面向对象语言,没有模板之类的东西。那么我们还有办法实现一套“通用”的链表API吗?答案当然是能——不要小看了C语言的黑魔法。
注意到,操作链表的函数做的只不过是摆弄next,prev两个指针而已,那么我们只要把这部分数据结构独立出来就好了。struct list_head { struct list_head *next, **prev; }; //然后要用到链表的地方: struct some_node { data_type data; struct list_head list; }; //如果你要把一个struct some_node插入链表,你给链表API传的参数长这样: int some_function() { struct some_node n; ... insert(&n->list); }
//举个例子 struct list_head *list_ptr = first(head);
注意到其实这个struct list_head *的指针指向的是struct some_node中间的一个位置,只要能知道偏移量,我们就能得到struct some_node的指针。当然,手算这个偏移量是要死人的,于是就有了这个:#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
#define offsetof(st, m) ((size_t)(&((st *)0)->m))
struct some_node *n = container_of(list_ptr, struct some_node, list);
Source: Linux/include/linux/list.h
3.
#define ngx_align(d, a) (((d) + (a - 1)) & ~(a - 1))