Linus slashdot:    https://meta.slashdot.org/story/12/10/11/0030249

原文: https://coolshell.cn/articles/8990.html

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;
elselist_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是否为链表头)就能移除某个节点,只要写)

*pp = entry->next

So there’s lots of pride in doing the small details right. It may not be big and important code, but I do like seeing code where people really thought about the details, and clearly also were thinking about the compiler being able to generate efficient code (rather than hoping that the compiler is so smart that it can make efficient code *despite* the state of the original source code). (纠正细节是令人自豪的事。也许这段代码并非庞大和重要,但我喜欢看那些注重代码细节的人写的代码,也就是清楚地了解如何才能编译出有效代码(而不是寄望于聪明的编译器来产生有效代码,即使是那些原始的汇编代码))。

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;elsehead = next;free(curr);}elseprev = 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);}elsecurr = &entry->next;}
}

同上一段代码有何改进呢?我们看到:不需要prev指针了,也不需要再去判断是否为链表头了,但是,curr变成了一个指向指针的指针。这正是这段程序的精妙之处。(注意,我所highlight的那三行代码)

让我们来人肉跑一下这个代码,对于——

  • 删除节点是表头的情况,输入参数中传入head的二级指针,在for循环里将其初始化curr,然后entry就是*head(*curr),我们马上删除它,那么第8行就等效于*head = (*head)->next,就是删除表头的实现。
  • 删除节点不是表头的情况,对于上面的代码,我们可以看到——

1)(第12行)如果不删除当前结点 —— curr保存的是当前结点next指针的地址。

2)(第5行) entry 保存了 *curr —— 这意味着在下一次循环:entry就是prev->next指针所指向的内存。

3)(第8行)删除结点:*curr = entry->next; —— 于是:prev->next 指向了 entry -> next;

是不是很巧妙?我们可以只用一个二级指针来操作链表,对所有节点都一样。

如果你对上面的代码和描述理解上有困难的话,你可以看看下图的示意:

二维指针删除单向链表相关推荐

  1. 利用二级指针删除单向链表

    http://coolshell.cn/articles/8990.html Linus举了一个单向链表的例子,但给出的代码太短了,一般的人很难搞明白这两个代码后面的含义.正好,有个编程爱好者阅读了这 ...

  2. 剑指offer面试题13扩展------Linus:利用二级指针删除单向链表

    Torvalds大婶:很多人不了解如何写核心底层代码 Torvalds大婶在slashdot上回答一些编程爱好者的提问,其中一个人问他什么样的代码是他所喜好的,大婶表述了自己一些观点之后,举了一个指针 ...

  3. C++链表插入节点函数为什么要传递头节点的二维指针

    C++链表插入一个节点的代码如下: struct ListNode {int m_value;ListNode * m_next; };void addListNode(ListNode** pHea ...

  4. 二维指针*(void **)的研究(uC/OS-II案例)

    uC/OS-II内存管理函数内最难理解的部分就是二维指针,本文以图文并茂的方式对二维指针进行了详细分析与讲解.看完本文,相信对C里面指针的概念又会有进一步的认识. 一.OSMemCreate( ) 函 ...

  5. OpenCV中图像Mat,二维指针和CxImage类之间的转换

    在做图像处理中,常用的函数接口有Opencv中的Mat图像类,有时候需要直接用二维指针开辟内存直接存储图像数据,有时候需要用到CxImage类存储图像.本文主要是总结下这三类存储方式之间的图像数据的转 ...

  6. 最长公共子序列的C++实现---附二维指针的使用方法

    想了挺久到底第一篇在这儿的博客写什么好,刚好这两天又一次看到动态规划的LCS算法觉得还是有点意思的,就拿来写了写,第一篇博客就发它吧. #include<iostream> #includ ...

  7. C++中给二维指针分配内存

          我们都知道在 C++ 中分配动态数组用的是 new , 撤销动态数组用的是 delete[ ] ,现在让我们来看看怎么利用这两个关键字给二维指针分配内存.       原理就不写在这里了, ...

  8. 二维指针动态分配内存连续问题分析

    当我们定义一个二维指针时,如果需要存储相应的数据,就需要我们动态的分配内存,这时,有一点是需要注意的,分配内存的方法不同,内存的连续性也是不相同的,首先,博主先贴出测试代码: #include < ...

  9. 如何把一个二维数组的地址赋给一个二维指针?

    int main() { int i = 0, j = 0; int arr[3][5] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; ...

最新文章

  1. Java基础篇:异常处理
  2. eeglab中文教程系列(8)-选择数据的epochs并进行比较
  3. NIST 人脸识别竞赛 FRVT(Face Recognition Vendor Test)
  4. mybaits二十三:二级缓存
  5. 八大排序算法(理论和动态图)
  6. PHP获取客户端的真实IP
  7. vba 将html转换excel,利用VBA将不同格式excel模板之间进行数据转换实例
  8. UnitOfWork知多少
  9. 说一下朗数可视化快速开发平台
  10. 地理信息考c语言,南师地理信息系统专业01方向真题C语言
  11. <Android开发> Android开发工具- 之-I2C TOOLS工具使用
  12. CISCO 命令手册
  13. php博饼,妙趣横生庆中秋:厦门博饼
  14. 每日一记:2017.12.1
  15. OSChina 周六乱弹 ——巴叔说他一直擅长硬来,弱弱的问……
  16. 裁剪用C语言,多边形裁剪
  17. 百度贴吧恶意代码分析
  18. %几.几//C语言(闲的没事,记录下)
  19. 响应式网页设计学习笔记
  20. 佳博80250打印机怎么看打印机IP

热门文章

  1. Python - selenium_WebDriver 鼠标键盘事件
  2. ueeditor 百度编译器使用onchange效果
  3. 微软职位内部推荐-Software Development Engineer II
  4. 正试图在 os 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码......
  5. Windows启动exe应用程序,无法正常启动(0xc000007b)的解决办法
  6. python udp广播_udp单播-广播-组播-python例子 | 学步园
  7. python删除指定位置的字符串_python去除区域 python删除字符串中指定位置字符
  8. 用sisotool设计调节参数_工业净化车间施工的重要参数
  9. ueditor上传组件显示乱码_最全面的移动端 UI组件设计详解:中篇
  10. epoch如何设置_使用TFRecordDataset时如何设置epoch计数器?