Linus Torvalds 在 TED 演讲上所说的有品味的代码

2022-05-19 22:02:14 +08:00
 Biwood

需求是从单向链表中删除一个指定节点。

教科书上的(普通的)写法:

void remove_cs101(list *l, list_item *target)
{
        list_item *cur = l->head, *prev = NULL;
        while (cur != target) {
                prev = cur;
                cur = cur->next;
        }
        if (prev)
                prev->next = cur->next;
        else
                l->head = cur->next;
}

优雅的(有品味的)写法:

void remove_elegant(list *l, list_item *target)
{
        list_item **p = &l->head;
        while (*p != target)
                p = &(*p)->next;
        *p = target->next;
}

目测充分利用的指针的特性,代码量少了不少。

代码仓库和详细解释在这里: https://github.com/mkirchner/linked-list-good-taste/blob/main/README.md

11492 次点击
所在节点    程序员
120 条回复
lonewolfakela
2022-05-20 11:05:19 +08:00
本质上和 OP 给的第二种写法是一回事,但是明显清楚多了……

void remove_elegant(list& l, list_item& target)
{
for (auto& it = l.head; l != nullptr; it = *(it->next))
{
if (it == target)
{
it = target->next;
break;
}
}
}

事实证明,没事不要写 C 语言,即使不用到各种 fancy 的工具,用 C++写出来的简单的 C with class & reference 代码也能好很多……
lonewolfakela
2022-05-20 11:10:25 +08:00
啊应该是 for (auto& it = *l.head; it.next != nullptr; it = *(it.next)) 不过这么搞就得额外判断最后一个元素了……
emmmm 好吧果然引用和指针混在一起还是八字不合。从一开始就应该好好地包装成迭代器才对……
ksco
2022-05-20 11:17:35 +08:00
某些人真是又菜又傲慢。我觉得自己写不出来优雅的代码不可怕,但拒绝接受优雅代码固步自封就挺可怕的。
rshun
2022-05-20 11:22:34 +08:00
第一种写法和第二种都能实现,但 linus 提供另外一种思路,我觉得这才是最重要的。
luxor
2022-05-20 11:37:19 +08:00
@greygoo 你确认?当 jne .L11 不跳转的时候,要执行 3 条指令,而“不优雅只需要执行 2 条指令”。
pastor
2022-05-20 12:55:14 +08:00
首先声明:我水平很一般。

正题:指针、二级指针在内核社区,或者在嵌入式领域,都是很常规的操作,而且主题部分的两段代码,第二段确实节省了语句指令、更简洁。应用层的 CV 程序员就不要乱说 linus 的不好了吧,首先提高下自己的水平再战好不好?又菜又不承认也就算了,还非要站出来去指责自己领域里神仙般的存在的代码,你们这些这样“我不要你觉得,我要我觉得。。。”的大神可真让人开眼。

@ColorfulBoar #29
1. linux 内核不支持 cpp ,所以请不要用 cpp 引用这种来对比内核代码了吧
2. auto p = list_head(&head); 如果我没看错的话,head 自己是指针,&head 是二级指针,这里相当于把&head 这个二级指针强转成一级指针了吧?所以后面的操作内存应该已经是乱掉了吧?我很久没写 c/c++了,不知道是不是我看错了,各位确认下吧
3. 这段代码,引用和指针都用,看上去比 linus 的版本更容易迷惑人,比如我 2 中提到的,然后还有人点赞。。。
4. 单从命名上讲,list_head 这个也不合理啊,next 之后它还是 head 吗?明明是 list_item 更合理好不好?
icyalala
2022-05-20 13:04:28 +08:00
来看看这些回复:
"第一种比第二种更好,因为第二种我看不懂"
"让更多人看懂的代码才是好代码"
"写优雅代码的我会炒掉,我们不写底层代码"
fnmain
2022-05-20 13:30:47 +08:00
站在不同人的角度,得出的结论可能是相反的,各方理由也都很有说服力。
只从可读性分析:
玩指针非常熟练的人,Linus 的方法更合适,因为少一个变量,占用头脑中寄存器的数量就少。
但如果解指针带来的脑力负担大于多维护一个变量的负担,就更适合教科书上的方法。
因此不同基础和编程习惯的人,对这个问题的看法会不一样。当遇到一个你不认可的观点,急于否定之前可以先思考一下对方这么想的原因,何况这个人还是 Linus ,或许值得你为此多花半分钟。
mingl0280
2022-05-20 13:43:43 +08:00
@icyalala 确实让更多人看懂的代码才是好代码,但是第二种根本不存在“看不懂”——看不懂的连本科生水平都当不到啊……是怎么混进程序员行业的?
banmuyutian
2022-05-20 13:46:21 +08:00
虽然我是 CRUD BOY ,但觉得第二种好
nevin47
2022-05-20 14:32:23 +08:00
@pastor #46 五年前我还没接触 Kernel 代码的时候我也会觉得第二种是异类

后来无意间涉足了内核,然后真的摸爬滚打了好几年,慢慢也就习惯了这种思维方式了(更贴合内存与硬件的思维形式)

其实这个是应用层开发与系统层开发的思维的区别,我们在公司内的一些研讨会上也会讨论类似的问题,“明明都是 C 语言,为什么底层软件的代码上层软件的工程师普遍觉得看不懂”。后来的结论就是思维方式不同导致了看不明白,就像是背完了英文单词,但是照样阅读理解做不及格。

当然,人们面对未知领域……天生存在着一些傲慢,也是正常表现😹
GopherDaily
2022-05-20 14:34:58 +08:00
如果团队的同学都能看得懂优雅的那种,我自然倾向于第二种。
实际情况是,大多数同学的水平和我们的产出都适合第一种。
crisrock
2022-05-20 14:48:06 +08:00
除了看不懂,第二种没毛病
dingwen07
2022-05-20 14:50:11 +08:00
其实第二种可读性也很高
xxv0
2022-05-20 15:06:08 +08:00
我感觉很多人看不懂都是因为没有直接把指针当作一个值来理解,看到指针考虑的都是指针指向的值。把 p 这个二级指针当作一级指针来看的话,把*p 当作一个普通的值,会发现第二种省去了很多中间变量,第二种可读性更高。
xxv0
2022-05-20 15:08:41 +08:00
就像是 @GeruzoniAnsasu 说的一样,把指针本身视为节点。
cloudsigma2022
2022-05-20 15:24:22 +08:00
为什么我看第一种,半天不知道它在绕什么,第二种秒懂呢?
pastor
2022-05-20 15:53:18 +08:00
@nevin47 #51 其实我怀疑那帮说 linus 写法不好的小年轻是不是连 linus 是谁都不知道。见惯了 ctrl+cv 程序员评价语言、框架、写法这不好那不好的,但 cv 程序员自己基本功都没扎实呢,所以幸好有 linus 之类的独裁型社区领袖,否则各种乱建议说不定就把好玩意搞砸了
whyso
2022-05-20 16:04:46 +08:00
雅到极致不风流
nevin47
2022-05-20 16:18:18 +08:00
@pastor #58 主要还是上层应用的编程思维和操作系统的编程思维的区别。

就像上面有层在吐槽 container_of 一样,实际上 container_of 是公认的一个精妙绝伦的设计,而且这个宏在 compile time 也有保护的。如果不是对编译原理、内存管理、C 语言基础极其精通,是不可能设计出这个宏的。如果一直带着上层应用:我们要简化代码、要防止任何异常输入,这种思维来看内核,那永远体会不到这些精妙的设计的意义

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/854016

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX