Chinaunix首页 | 论坛 | 博客
  • 博客访问: 188681
  • 博文数量: 27
  • 博客积分: 725
  • 博客等级: 上士
  • 技术积分: 347
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-04 09:01
文章分类

全部博文(27)

文章存档

2012年(15)

2011年(12)

分类: LINUX

2011-11-27 22:34:05

最近在看《LINUX内核源代码情景分析》存储管理方面的内容,大量涉及到链表方面的操作,其中有一段代码挺有意思,是从__insert_vm_struct函数中截取的关于单链表操作的,代码如下:
  1.     struct vm_area_struct **pprev;
  2.     
  3.     pprev = &mm->mmap;
  4.     while (*pprev && (*pprev)->vm_start <= vmp->vm_start)
  5.         pprev = &(*pprev)->vm_next;
  6.     
  7.     vmp->vm_next = *pprev;
  8.     *pprev = vmp;

大意就是向一个升序单链表中插入一个节点,但是采用的操作方法很特别。为便于描述该机理,写了如下简化代码:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stdarg.h>

  4. static struct node_t {
  5.     struct data *head;
  6. }node, *pnode = &node;

  7. struct data {
  8.     struct data *next;
  9.     int n;
  10. };

  11. void _insert(struct data *pdata)
  12. {
  13.     struct data **ppdata = &pnode->head;
  14.     while (*ppdata && (*ppdata)->n < pdata->n)
  15.         ppdata = &(*ppdata)->next;
  16.     pdata->next = *ppdata;
  17.     *ppdata = pdata;
  18. }

  19. void insert(int n)
  20. {
  21.     struct data *t = calloc(1, sizeof *t);
  22.     t->n = n;
  23.     _insert(t);
  24. }

  25. void var_insert(int n, ...)
  26. {
  27.     va_list ap;
  28.     va_start(ap, n);
  29.     if (n != -1) insert(n);
  30.     else return;
  31.     while (1) {
  32.         int i = va_arg(ap, int);
  33.         if (i == -1) break;
  34.         insert(i);
  35.     }
  36.     va_end(ap);
  37. }

  38. void show(void)
  39. {
  40.     struct data *t = pnode->head;
  41.     for ( ; t; t = t->next) {
  42.         printf("%d\t", t->n);
  43.     }
  44.     printf("\n");
  45. }

  46. int main(void)
  47. {
  48.     var_insert(5,9,3,12,1,16,4,7,-1);
  49.     show();
  50.     return 0;
  51. }

关键在于_insert函数,定义了一个二级指针,可以通过该变量同时获取三个结点,

  1. #define list_entry(ptr, type, member) \
  2.     ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

获取上一个结点:list_entry(ppdata, struct data, next);

获取当前结点: *ppdata

获取下一个结点: (*ppdata)->next

可见该二级指针的运用相当灵活,从链表中删除一个结点操作如下:

  1. void delete(int n)
  2. {
  3.     struct data **ppdata = &pnode->head;
  4.     while (*ppdata && (*ppdata)->n != n)
  5.         ppdata = &(*ppdata)->next;
  6.     *ppdata = (*ppdata)->next;
  7.     ppdata = &(*ppdata)->next;
  8. }
阅读(3044) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~