在阅读源代码或设计文档时,看到惊艳的技巧

提两个C语言里实现列表用到的技巧。会用的人可能会觉得很平常,但是第一次看到的时候还是觉得很厉害的:
  1. 两级指针两级指针要怎么用呢?我们先来看一下大部分正常人实现列表的方法:
  2. //定义链表节点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);
    }
    
    有没有觉得remove里这个if很麻烦?有没有办法不用条件语句就处理所有情况?请看:
    //这一次,我们把节点定义成这样
    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);
    }
    
    这里给的例子不是很全,事实上这种做法可以在多种链表操作里简化对head的判断。至于这个方法是怎么工作的,可以自己想想,并不难理解。
    //同样的技巧还能用在单链表的删除节点上
    //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;
    	}
    }
    
    Source: <sys/queue.h>
  3. 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 *类型的,怎么把它还原到struct some_node呢?
    //举个例子
    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) );})
    
    这个宏的意思是说,给定ptr是指向type中间一个member的指针,获取type本身的指针。其中的核心是offsetof这个宏,功能是取得type中member的offset,定义是这样的:
    #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))

现在你们知道C是一个充满闪(hei)光(mo)点(fa)的语言了吧……
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、开发背景 传统的合同管理,多采用手工的形式,既繁琐又易于出差错,随着电子技术的发展,合同进行信息化管理,避免进行简单的重复,从而做到准确、快捷。为了适应这个要求,我们工作组经过详细的市场调查,发现市面上合同软件繁多,竞争激烈,为了避免重复生产类似的软件产品,做出自己的特色,我们决定做一个切合单位合同管理实际要求的合同管理系统软件。 二、选题的意义 为了更好地适应工作人员对合同管理系统的需求,缓解手工管理存在的弊端,开发合同管理系统。合同管理系统向用户提供的服务将在传统的“录入-修改-删除-查找”基础上,进一步提供全方位的信息服务。它具有以下几个特点: (1)可以存储所有合同的资料,具有安全、高效的特性; (2)只需1名合同信息录入人员即可操作本合同管理系统,可以节省大量的人力和物力。 (3)可以通过查询系统迅速查到所需要的信息。 在对合同管理系统的流程进行认真系统的分析后,我认为本系统用户的需求可以分为3个方面:第1方面是用户登录管理。只有有权限的用户才能进入本系统,没有权限的用户或非法用户不能进入本系统,从而有效地保证系统的安全。第2方面是合同信息的查找。能够对合同的具体信息进行查找。可以提供按间范围查找、按所属部门查找、按合同编号查找、按客户名称查找、按合同类别查找、按模糊条件查找。第3方面也是合同管理系统的核心工作,即合同基本信息录入。能够对合同的基本信息进入录入,包括合同的编号、合同的类别、合同名称、合同部门、所属部门、合同开始日期、合同结束日期、合同额等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值