当然,我学的第一门编程语言是C语言(虽然现在很少使用C语言了,都是用一些抽象做的很高的语言),对于C语言,我是很、非常、及其的认真的学习,因为C是能够让你非常贴切的认识各种东西的语言,通过C语言你可以了解到编译原理的一些知识,可以辅助你读操作系统的源码,可以写一些高效的代码,当你学习汇编语言的时候,你会发现C语言与汇编语言的千丝万缕的联系,当你学习C++的时候的有C的基础是非常必要的。所以如果想学好编程,了解C语言是非常的必要的。
当然每个学习过C的人都曾被C的指针折磨过(不过俺可没有,因为俺到后面关于指针的本质问题了解了,所以就可以以不变应万变了),虽说指针的原理我是非常的懂了,但是关于指针的一些用法,尤其是一些比较高端的用法,就拿二级指针来说吧,我总是觉得非要间接两次才能取到变量有啥意义,不过今天突然看到了一个使用二级指针的操作,非常好,在此分享一下。
指针的一个重要的使用点是对于链表的操作。语言链表如果删除一个指定的节点的操作,通常的解决方案如下:
typedef struct node { struct node *next; //satellite data... int data; }Node; typdef bool (* rmNode)(const Node *v); Node * removeIf(Node *head, rmNode rmFunc) { for(Node *pre=NULL, *current=head; current!=NULL;) { Node *next=current->next; if(rmNode(current)) { if(pre==NULL) { head=next; } else { pre->next=next; } free(current); } else { pre=current; current=current->next; } } return head; }
对于这个解决方案,你需要维护三个指针pre,current,next, 而且需要对head被删除做特殊的考虑,那么每次循环都会有判断的开销。
而使用二级指针的解决方案可以做成如下的效果:
void removeIf(Node **head, rmNode rmFunc) { for(Node** current=head; *current!=NULL) { Node *entry=(*current)->next; if(rmNode(entry)) { (*current)=entry->next; free(current); } else { current=&(entry->next); } } }
首先使用指针减少为两个了:current,entry ,而且不需要考虑首节点的情况,效率提高了。但是这个代码如果你对二级指针的理解不是很清楚的情况下,那么就不是特别的易读,当然你懂了那就另说了(虽然我懂了,但是感觉不写上一两百遍感觉还是不是特别容易的编程肌肉记忆)。
附注:这个可以linux内核当中实用的单项,双向链表中经常用到的技巧,而且linus在新闻组还说过的。这个技巧是我从coolshell上面看到的,coolshell(www.coolshell.cn)是一个特别好的技术blog,里面有很多不错的东西,很是值得学习,而且可以放到rss上。