二维指针删除单向链表
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;
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;
}
但在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;
是不是很巧妙?我们可以只用一个二级指针来操作链表,对所有节点都一样。
如果你对上面的代码和描述理解上有困难的话,你可以看看下图的示意:
相关文章:

对比java_java集合对比
list与Set、Map区别及适用场景1、List,Set都是继承自Collection接口,Map则不是2、List特点:元素有放入顺序,元素可重复 ,Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,(注…

.ARM.exidx
简介: .ARM.exidx is the section containing information for unwinding the stack. If your C program has functions that print out a stack backtrace, the functions will likely depend on this section being present. 相关的编译选项 -funwind-tables 二问…

Oracle VM VirtualBox安裝Windows 2000失败
问题:VirtualBox下安装Windows2000,设置网络后进入最后一步,复制组件……然后就是重启;再试还是重启!解决:在Oracle网站上查了一下资料:http://www.virtualbox.org/manual/ch12.html#idp1278616…

用户/目录操作
用户操作 useradd/adduser 创建用户 passwd 修改用户密码 userdel 删除用户 usermod 修改用户信息 -g<群组> 修改用户所属群组 -G<群组> 修改用户所属的附加群组 -l<帐户名> 修改账户名称 -u 修改用户ID -L锁定用户密码 -U 解除密码锁定 adduser -u用…
linux内核 -内存管理模块概图
1.从进程(task)的角度来看内存管理 每个进程对应一个task_struct;每个task_struct 里面包含指向mm_struct 的指针mm, mm_struct 里面的主要成员: a. 指向vma链表的头指针:mmap b. 指向vma红黑树的根节点: mm_rb c. 指向进程列表的指针pgb;vma(vm_are…

求一个字符串中连续出现的次数最多的子串
求一个字符串中连续出现的次数最多的子串。例如字符串“abababc”,最多连续出现的为ab,连续出现三次。要和求一个字符串中的最长重复子串区分开来,还是上面的字符串,那么最长的重复子串为abab。两个题目的解法有些类似,都用到了后…

java ftp 判断文件是否存在_FTP判断文件是否存在
FTP Client使用的是Apache Commons Net 3.3/*** 检查FTP上指定文件是否存在* param remoteFilePartNameList 文件路径* throws BusinessException* throws IOException*/private void checkFtpFileExist(List remoteFilePartNameList) throws BusinessException, IOException {…

软件定义光网络-SDON
为什么80%的码农都做不了架构师?>>> 软件定义光网络-SDON 随着宽带业务与应用的持续增长,光网络面临着新的发展机遇与技术挑战。作为当前业界研究热点之一,SDON聚焦于将软件定义技术融入光网络的综合解决方案,其关键技…

记录一次爬取某昵称网站的爬虫
同学跑去实习了...然后工作的时候要她用python写一个爬虫,爬取一万个可以用的用户昵称。(为什么他们都能找到工作啊QAQ) 然后,她找到了我...然后在我动笔的时候,发现之前写过的爬虫基本上忘完了...无奈下只好对着以前…

《LINUX3.0内核源代码分析》第一章:内存寻址
https://blog.csdn.net/ekenlinbing/article/details/7613334 摘要:本章主要介绍了LINUX3.0内存寻址方面的内容,重点对follow_page函数进行注释,以帮助读者大致了解ARM A9的页表组织。 读者需要理解一些基本概念:虚拟地址、物理地…

java integer int 比较_java Integer和int之间的比较问题是什么?
展开全部java Integer和int之间e68a84e8a2ad3231313335323631343130323136353331333365633864的比较问题。求解释public static void main(String[] args) { // TODO Auto-generated method stub Integer a new Integer(1); Integer b new Integer(1); int c1; Integer e 1;…

Oracle 12C -- 基于sequence的列的默认值
12C支持先创建一个sequence,然后再将该sequence指定为某个列的值的默认表达式。 和"identity column"具有以下不同点: 对列的个数没有限制 sequence必须在列定义之前定义 如果删除了sequence,会导致后面的insert报错 表的owner&…

Python的XML-RPC学习
编写客户端提交数据到服务器处理是程序员最常碰到的几个问题之一。各种不同的语言对此都有相应的解决方案。比如Unix下,C程序员们可以用SUNRPC,Java程序员则使用RMI来处理。大多数语言还都可以使用Web Service或者ICE。它们的使用方法类似,编…

Anaconda安装,jupyter notebook 使用说明
conda install pandas---安装pandas包 conda remove package_names conda update package_names conda list ---列出该环境下安装的package conda install nb_conda --------安装nb_conda用于notebook自动关联nb_conda的环境 conda create -n env_name package_name -------…
ARM32页表-虚拟地址到物理地址的转换
ARM32的页表 页表就是用于将虚拟地址转换为物理地址的转换关系表。访问虚拟地址时,计算机通过页表找到对应的实际物理地址访问。 我们在上一节介绍了内存管理模块概图, 怎么完成从pgd 到 page的转化呢? linux 内核code是通过follow_page来完成的…

java 重载 参数子类_java - Java中带有子类参数的函数重载 - 堆栈内存溢出
这个问题已经在这里有了答案:我有一个扩展了另一个类的类(在这种情况下,这是一个例外):public class NewTypeException extends Exception {private String exceptionField;public String getExceptionField() {return exceptionField;}publi…

Caused by: java.sql.BatchUpdateException
Caused by: java.sql.BatchUpdateException: Table (%s) has been dropped, altered or renamed.解决方法重启项目转载于:https://www.cnblogs.com/mySummer/p/4723561.html

do{ ...}while(0)应用技巧
辅助定义复杂的宏example: #define A(args) do { a(args); b() } while(0);如果定义#define A(args) a(args);b();if(i > 0) A(i) if(i > 0 )do { a(2);b();} while(0) 或者while(1)a(args);b(); 这不是我们想要的,因为第二个b();不会被执行。代替g…

Idea--使用Idea调试设置
参考 https://blog.csdn.net/yyjava/article/details/81453748 关闭一些Idea默认设置,否则懵逼到爆炸.. 1.关闭集合类视图 2.关闭watch视窗默认调用toString(真的很懵逼!!) 转载于:https://www.cnblogs.com/microcat/p…

基于i2c子系统的驱动分析
https://blog.csdn.net/qq_28992301/article/details/52467766

creo JAVA_Creo 4.0二次开发工具框架搭建
一、新建MFC DLL工程二、配置项目属性附加依赖项中输入:netapi32.lib;psapi.lib;mpr.lib;wsock32.lib;protk_dll_NU.lib;protk_dllmd_NU.lib;protkmd_NU.lib;protoolkit_NU.lib;pt_asynchronous.lib;ptasyncmd.lib;ucore.lib;udata.lib;尝试编译工程,如果…

html5 FileReader初识
使用html5的FileReader可以实现多媒体文件的预览功能,代码如下: <html> <head> <script type"text/javascript"> var fileReader new FileReader(); fileReader.onload function(event) {document.getElementById(image).…

puppet aix之自动化用户管理
一、 用户组的管理 (一) Puppet组管理特性 1. manages_aix_lam用来管理AIX的LAM(Loadable Authentication Module)系统。 2. manages_members对于目录服务是组属性成员,而不是用户。 3. system_groups用来允许你创建比较小GID的系统组,一般小…

C# 文件操作
C# 文件操作文件操作: 检查 创建 读取 写入 修改 删除目录操作: 检查 创建 读取 写入 修改 删除文件操作 若要执行此操作...请参阅本主题中的示例...创建文本文件向文件写入文本写入文本文件向文件写入文本读取文本文件从文件读取文本向文件中追加文本File.AppendText FileInfo…
一种内存池管理技术
本文介绍一种内存池管理技术。 在m公司工作了4年多,一直负责内存池模块问题的处理,比如内存越界,data abort 系统异常的处理,本文加以总结,以便后续参考。 读本文之前,先有个约定,本文中提到的p…

企业支付宝账号开发接口实现
转载自:http://my.oschina.net/xshuai/blog/313809 关于即时到账的开发。审核通过。简单测试如下。 希望看的可以收藏或者赞一下哦。 1:拥有自己的支付宝企业账号。去产品商店选择适合自己的方案。并签约合同。 2:选择合适的商家收款产品并去签约。填写相应的信息 3…
Fastadmin管理Mysql_FastAdmin-CMS模版制作(6)-正式部署
一、工具信息介绍(1)服务器系统:CentOS7.2 64位系统;(2)服务器面板:宝塔,官网地址:https://www.bt.cn/;(3)PHP7.2;(4)mysql5.6;(5)Nginx;二、运行环境安装(1)进入宝塔官网…

使用微软提供的Office Online实现Office文档的在线查看,编辑等功能
使用微软提供的Office Online平台只需要一个网址即可在线查看Xls,doc,PPT等文档http://view.officeapps.live.com/op/view.aspx?src要查看的文档地址在线编辑需要登录live.com并从onedrive中打开或新建文档也可以来自在线模板(下面的Excel来自Excel Online模板,编辑…

HDU(1847)Good Luck in CET-4 Everybody!
利用PN分析求解此题。递推下去会发现3和3的倍数都是P点。 #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <set> using namespace std; int main() {int n;while(~scanf("%d",&n)){i…
uboot引导kernel - 1 - Flash的分区
uboot启动Linux内核过程分为4大步骤: 问题1:Flash的分区相关问题 在 上述步骤1/2/4 中都提到了从启动介质(iNand/SD)中读取uboot/kernel到SRAM/DDR中,那么具体从启动介质的什么位置分别读取呢? 上述步骤1中,iROM的…