Linus:利用二级指针删除单向链表
来源:互联网 发布:mac os x 10.8 下载 编辑:程序博客网 时间:2024/04/28 22:52
Linus大神在slashdot上回答一些编程爱好者的提问,其中一个人问他什么样的代码是他所喜好的,大婶表述了自己一些观点之后,举了一个指针的例子,解释了什么才是core low-level coding。
下面是Linus的教学原文及翻译——
“At the opposite end of the spectrum, I actually wish more people understood the really core low-level kind of coding. Not big, complex stuff like the lockless name lookup, but simply good use of pointers-to-pointers etc. For example, I’ve seen too many people who delete a singly-linked list entry by keeping track of the “prev” entry, and then to delete the entry, doing something like。(在这段话的最后,我实际上希望更多的人了解什么是真正的核心底层代码。这并不像无锁文件名查询(注:可能是git源码里的设计)那样庞大、复杂,只是仅仅像诸如使用二级指针那样简单的技术。例如,我见过很多人在删除一个单项链表的时候,维护了一个”prev”表项指针,然后删除当前表项,就像这样)”
if
(prev)
prev->next = entry->next;
else
list_head = entry->next;
“and whenever I see code like that, I just go “This person doesn’t understand pointers”. And it’s sadly quite common.(当我看到这样的代码时,我就会想“这个人不了解指针”。令人难过的是这太常见了。) ”
“People who understand pointers just use a “pointer to the entry pointer”, and initialize that with the address of the list_head. And then as they traverse the list, they can remove the entry without using any conditionals, by just doing a “*pp = entry->next”. (了解指针的人会使用链表头的地址来初始化一个“指向节点指针的指针”。当遍历链表的时候,可以不用任何条件判断(注:指prev是否为链表头)就能移除某个节点,只要写)”
Linus举了一个单向链表的例子,但给出的代码太短了,一般的人很难搞明白这两个代码后面的含义。正好,有个编程爱好者阅读了这段话,并给出了一个比较完整的代码。他的话我就不翻译了,下面给出代码说明。
如果我们需要写一个remove_if(link*, rm_cond_func*)的函数,也就是传入一个单向链表,和一个自定义的是否删除的函数,然后返回处理后的链接。
这个代码不难,基本上所有的教科书都会提供下面的代码示例,而这种写法也是大公司的面试题标准模板:
typedef
struct
node
{
struct
node * next;
....
} node;
typedef
bool
(* remove_fn)(node
const
* v);
// Remove all nodes from the supplied list for which the
// supplied remove function returns true.
// Returns the new head of the list.
node * remove_if(node * head, remove_fn rm)
{
for
(node * prev = NULL, * curr = head; curr != NULL; )
{
node *
const
next = curr->next;
if
(rm(curr))
{
if
(prev)
prev->next = next;
else
head = next;
free
(curr);
}
else
prev = curr;
curr = next;
}
return
head;
}
这里remove_fn由调用者提供的一个是否删除当前实体结点的函数指针,其会判断删除条件是否成立。这段代码维护了两个节点指针prev和curr,标准的教科书写法——删除当前结点时,需要一个previous的指针,并且还要这里还需要做一个边界条件的判断——curr是否为链表头。于是,要删除一个节点(不是表头),只要将前一个节点的next指向当前节点的next指向的对象,即下一个节点(即:prev->next = curr->next),然后释放当前节点。
但在Linus看来,这是不懂指针的人的做法。那么,什么是core low-level coding呢?那就是有效地利用二级指针,将其作为管理和操作链表的首要选项。代码如下:
void
remove_if(node ** head, remove_fn rm)
{
for
(node** curr = head; *curr; )
{
node * entry = *curr;
if
(rm(entry))
{
*curr = entry->next;
free
(entry);
}
else
curr = &entry->next;
}
}
同上一段代码有何改进呢?我们看到:不需要prev指针了,也不需要再去判断是否为链表头了,但是,curr变成了一个指向指针的指针。这正是这段程序的精妙之处。(注意,我所highlight的那三行代码)
让我们来人肉跑一下这个代码,对于——
- 删除节点是表头的情况,输入参数中传入head的二级指针,在for循环里将其初始化curr,然后entry就是*head(*curr),我们马上删除它,那么第8行就等效于*head = (*head)->next,就是删除表头的实现。
- 删除节点不是表头的情况,对于上面的代码,我们可以看到——
- (第12行)如果不删除当前结点 —— curr保存的是当前结点next指针的地址。
- (第5行) entry 保存了 *curr —— 这意味着在下一次循环:entry就是prev->next指针所指向的内存。
- (第8行)删除结点:*curr = entry->next; —— 于是:prev->next 指向了 entry -> next;
是不是很巧妙?我们可以只用一个二级指针来操作链表,对所有节点都一样。
如果你对上面的代码和描述理解上有困难的话,你可以看看下图的示意:
ref: http://coolshell.cn/articles/8990.html
- Linus:利用二级指针删除单向链表
- Linus:利用二级指针删除单向链表
- Linus:利用二级指针删除单向链表
- Linus:利用二级指针删除单向链表-实例代码
- Linus:利用二级指针删除单向链表
- Linus:利用二级指针删除单向链表
- Linus:利用二级指针删除单向链表
- Linus:利用二级指针删除单向链表
- Linus:利用二级指针删除单向链表
- Linus:利用二级指针删除单向链表
- Linus:利用二级指针删除单向链表
- Linus:利用二级指针删除单向链表
- Linus:利用二级指针删除单向链表
- Linus:利用二级指针删除单向链表
- Linus:利用二级指针删除单向链表
- Linus:利用二级指针删除单向链表
- Linus:利用二级指针删除单向链表
- Linus:利用二级指针删除单向链表 转自酷壳 – CoolShell.cn
- 定时自动备份数据库
- pppoe pppd连接不上问题
- 网络营销软件有哪些?营销软件首选来客易!
- 【设计模式】策略模式Strategy_03
- 【九度】【1507】不用加减乘除做加法
- Linus:利用二级指针删除单向链表
- 牛腩新闻系统(四)——类生成器生成实体代码
- 将标准输入复制到标准输出
- JAVA中的endorsed技术
- Stanford机器学习 -- Linear Regression with one variable
- 开源免费的天气预报接口API以及全国所有地区代码(国家气象局提供)
- 链表和数组的区别在哪里
- iOS下的RSA加密方法
- 备考GRE写作技巧之多背诵GRE作文的经典句子