回调函数编写双向链表求和、找最大值学习笔记(摘抄自李先静博客)

1、自己之前在开发功能时候也常常封装很多函数。这种做法会造成重复的代码,让dlist的实现随着应用环境的变化而变化。

2、采样上一篇的回调函数法。这两个函数的实现和dlist_print的实现类似,无非是print那行代码要换成别的功能。在真正动手时,发现每个回调函数都要保存一些中间数据。大部分人选择了用全局变量来保存,这可以实现要求的功能(这也是我常常犯的毛病),但违背了禁用全局变量的原则。

(1).不要编写重复的代码。

写重复的代码很简单,甚至凭本能都可以写出来。但要想成为优秀的程序员,一定要克服自己的惰性,因为重复的代码造成很多问题:

重复的代码更容易出错。在写类似代码的时候,几乎所有人都会选择 Copy&Paste 的方法,这种方法很容易犯一些细节上的错误,如果某个地方修改不完整,那就留下了”不定时”的炸弹,说不定什么时候会暴露出来。

重复的代码经不起变化。无论是修改BUG,还是增加新特性,往往你要修改很多地方,如果忘掉其中之一,你同样得为此付出代价。请记住古惑仔的话,出来混迟早是要还的。大师们说过,在软件中欠下的BUG,你会为此还得更多。

去除重复代码往往不是件简单的事情,需要更多思考和更多精力,不过事实证明这是最值得的投资。在这里,我们要怎么抽取这些重复的代码呢?
这三个函数无非是要遍历双向链表并做一些事情,遍历双向链表我们可以提供一个dlist_foreach 函数,至于要做什么,这是千变万化的行为,可以通过回调函数让调用者去做。

(2)、任何回调函数都要有上下文

虽然混了这么多年嵌入式开发软件的饭,但是自己还是常常喜欢用全局变量;使用全局变量有很多坏处,按作者的说法列举如下:

(禁止全局变量
除了为使用单件模式(只允许一个实例存在)的情况外,任何时候都要禁止使用全局变量。这一点我反复的强调,但发现初学者还是屡禁不止,为了贪图方便而使用全局变量。请读者从现在开始就记住这一准则。
全局变量始终都会占用内存空间,共享库的全局变量是按页分配的,那怕只有一个字节的全局变量也占用一个page,所以这会造成不必要空间浪费。全局变量也会给程序并发造成困难,想把程序从单线程改为多线程将会遇到麻烦。重要的是,如果调用者直接访问这些全局变量,会造成调用者和实现者之间的耦合。)

在使用回调函数情况下可以这样避免使用全局变量:很简单,给回调函数传递额外的参数就行了。这个参数我们称为回调函数的上下文,变量名用ctx(context的缩写)。要在这个上下文中存放什么东西呢?那得根据具体的回调函数而定,为了能保存任何数据类型,我们选择void*表示这个上下文。

DListRet dlist_foreach(DList* list,DListDataVisitFunc visit,void* ctx)

{

   DListNode* node = list->first;

   while(node != NULL)

   {

      visit(ctx,node->data);

      node =  node->next;

  }

   return DLIST_RET_OK;

}

visit 是回调函数,ctx就是我们说的上下文。要特别强调的一点是,ctx应该作为回调函数的第一个参数。为什么呢?在前面我们讲过的面向对象的函数命名规则中,我们以thiz作为函数的第一个参数,而thiz通常也就是函数的上下文。如果在这里恰ctx==thiz,就不需要因为参数顺序不同而做转换了。

实现求和的回调函数:

DListRet sum_cx(void* ctx,void* data)

{

   long* sum = ctx;

   *sum += (int)data;

   return DLIST_RET_OK;

}

调用foreach:

long sumx = 0;

dlist_foreach(dlist,sum_cx,&sumx);

同理,查找最大值的回调函数:

typedef struct _Max_Val

{

   unsigned char first;

   int max_val;

}Max_Val;

DListRet max_cx(void* ctx,void* data)

{

   Max_Val* max = ctx;

   if(ctx->first == 0)

   {

      ctx->first = 1;

      max->max_val = (int)data;

   }

   else if(max->max_val < (int)data)

    {

      max->max_val = (int)data;

    }

    return DLIST_RET_OK;

}

调用foreach:

Max_Val maxval = {.first = 0,0};

dlistforeach(dlist,max_cx,&maxval);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值