深度复盘Linux之父对C++的「炮轰」史:C++究竟为何被“拉黑”?

C语言与CPP编程 2025-02-20 09:02

击上方“C语言与CPP编程”,选择“关注/置顶/星标公众号

干货福利,第一时间送达!

最近有小伙伴说没有收到当天的文章推送,这是因为微信更改了推送机制,导致没有星标公众号的小伙伴刷不到当天推送的文章,无法接收到一些比较实用的知识和资讯。所以建议大家加个星标⭐️,以后就能第一时间收到推送了。


转自 | CSDN(ID:CSDNnews) 作者 | Jan Kammerath     翻译 | 郑丽媛 

【CSDN 编者按】在软件开发领域,关于编程语言的选择一直是开发者们热议的话题。尤其是对于像 Linux 内核这样对性能和稳定性要求极高的项目,语言的选择更是至关重要。Linux 内核创始人 Linus Torvalds,长期以来对 C++ 持批评态度,并明确拒绝将其用于 Linux 内核开发。在本文中,作者将深入探讨 Linus Torvalds 对 C++ 的批评,分析其背后的技术原因,并思考这些观点对现代软件开发的启示。


多年来,Linux 的创始人 Linus Torvalds 一直对 C++ 持批评态度,并且明确拒绝将其用于 Linux 内核开发。他不仅公开表达了自己的观点,还提出了许多值得深入探讨的反对使用 C++ 的理由。

虽然 C 和 C++ 几乎相同,但实际上它们并不一样。C++ 通常被认为是 C 的面向对象“版本”,或者说是 C 的继承者。但实际上,C++ 是对 C 语言的一种扩展,引入了面向对象、构造函数、析构函数、模板、异常处理、命名空间和运算符重载等特性。所有这些扩展都带来了各自独特的挑战和编程范式——不出所料,Linus 反对 C++ 的技术论点也都源于这些扩展。

图片


图片

Linus 对 C++ 的主要批评

Linus 提出的反对 C++ 的主要论点,可以在内核邮件列表中的 "编译 C++ 内核模块 + Makefile "一栏中找到,相关回应最早可以追溯到 2004 年。接下来,让我们撇开这些邮件消息中的个人争论,专注于论点本身。

(1)C++ 中的异常处理

“整个 C++ 的异常处理机制从根本上就是错误的。对于内核来说,它更是错得离谱。”

C 语言依赖返回值来指示错误,而 C++ 则倾向于使用异常。异常可以在代码的任何部分抛出,这使得抛出异常的代码在某种程度上变得不可预测。它们是非确定性的,并为 C++ 带来了完全不同的错误处理范式。错误处理是编程的基本需求,而 C++ 的不同方法本质上使其成为了一种完全不同的编程语言。

与 C 语言简单的返回值方法相比,异常的不可预测性和实现方式的多变性使得调试异常非常困难。如果将异常引入 Linux 内核,那么在将所有内容迁移到 C++ 的若干年里,内核将至少有两种不同的错误处理方式。

虽然这对于编写 GNOME 的绘画应用程序来说并不是什么问题,但对于拥有 3000 万行代码的 Linux 内核来说,这确实是一个真正的风险和威胁。在 Linux 内核中引入异常处理,将不可避免地导致内核变得更加不稳定。尽管内核变更的测试和审批过程非常严格,但要完全避免所有错误是不可能的。

(2)C++ 编译器中的内存管理

“任何喜欢在背后隐藏内存分配等操作的编译器或语言,都不是内核开发的好选择。”

异常处理确实带来了一些由编译器隐藏起来的开销。这个关于“隐藏内存管理”的论点,指向 RAII(资源获取即初始化)或 C++ 中通过析构函数进行的自动内存管理。

Linux 内核的内存管理非常精细且经过高度优化,其中许多性能优化已经将编译器推向了极致。在内核开发团队已经面临编译器挑战的情况下,引入这种自动化的编译器特性是有风险的。

这不仅会导致使用 RAII 的模块性能下降,还会引入一个新的依赖关系。Linux 内核将更加依赖编译器。尽管这种依赖非常小且微不足道,但对于一个在全球数十亿设备上运行的 3000 万行代码库来说,这就是一个很大的问题。

(3)C++ 与 C 中的面向对象编程

“你可以在 C 语言中编写面向对象的代码(对系统文件等场景很有用),而无需使用 C++中的那些垃圾。”

面向对象编程(OOP)是支持使用 C++ 而非纯 C 的主要论点,然而在 Linus 看来,相较于 C++ 带来的复杂性和风险这并不值得。他认为在 C 语言中也可以实现基本的面向对象编程:通过使用模仿 C++ 类的结构体(structs),可以在纯 C 中实现面向对象的概念。以下是一个非常简单的例子,展示了如何在纯 C 中实现面向对象编程:


#include #include 
// Define the "class" using a structtypedef struct { int value; // Function pointer for a method void (*increment)(struct Person *self);} Person;
// Method implementation for incrementvoid increment_person(Person *self) { self->value++;}
// Constructor-like function to initialize a PersonPerson* person_new(int initial_value) { Person *p = (Person*)malloc(sizeof(Person)); p->value = initial_value; p->increment = increment_person; return p;}
// Destructor-like functionvoid person_free(Person *p) { free(p);}
int main() { // Creating an instance of Person Person *p = person_new(5); // Using the method printf("Initial value: %d\n", p->value); p->increment(p); // Incrementing the value printf("After increment: %d\n", p->value);
// Freeing the allocated memory person_free(p); return 0;}


面向对象编程并不是 Linux 内核运行的技术要求,它只是一个不同的编程范式或概念。将 OOP 引入 Linux 内核的目的是为了提高模块化、封装性和代码复用性,从而简化维护和开发。这些优点都属于开发者体验的范畴。

Linux 内核之所以在全球数十亿设备上广泛采用,是因为其性能和稳定性。而这些优势是以牺牲开发者体验和开发周期为代价的:任何旨在改善开发者体验的改进都会不可避免地削弱内核的性能和稳定性。性能、稳定性和易用性之间总是需要彼此权衡。

因此,Linux 内核开发在很大程度上忽视了开发者体验,以实现尽可能更高的稳定性和性能——这是内核的设计哲学。因此,引入 OOP 不仅是一种风险和威胁,更是对内核哲学的彻底改变。Linux 用户,尤其是在关键任务环境中的用户,既不会欢迎也不会容忍这种改变。这也再次证明了 Linus 的观点是有道理的。

(4)C++ 库和依赖项的稳定性

“当这些库无法工作时,会带来无尽的痛苦(任何告诉我 STL 和 Boost 是稳定和可移植的人,简直是满嘴跑火车,而且一点都不好笑)。”

在用户级或用户空间应用程序开发的背景下,Boost 和 STL 可以被认为是“稳定的”。然而,当涉及到内核开发以及 Linux 内核的要求时,“稳定”这个词有着更为严格的意义。

提出使用 C++ 的标准模板库(STL)或 Boost 库,其目的是为了改善开发者体验。然而,引入这些依赖项所带来的风险与 RAII 相同,都会以牺牲性能和稳定性为代价。

除了性能和稳定性之外,还有一个所有权的问题。内核引入的每一个依赖项,都会将责任转移给该依赖的维护者。随着责任的转移,所有权也会转移,这就带来了安全风险。例如 CVE-2024–3094 漏洞,这是由一个名为“Jia Tan”的神秘用户在 liblzma 库中引入的一个后门。

(5)低效且臃肿的抽象

“低效的抽象编程模型,两年后你会发现某些抽象并不高效,但此时你的所有代码都依赖于这些漂亮的对象模型,而你无法修复它,除非重写你的应用程序。”

这个观点与 C++ 本身关系不大,而是针对面向对象编程和继承的概念。面向对象编程中的设计模式旨在提供解决常见问题的蓝图方案,这是有用的,所有进入 OOP 领域的人都应该注意。内核开发人员是非常有能力的软件工程师和程序员,他们知道如何在何时何地应用 OOP——然而,他们也清楚 OOP 并非没有缺点。除了技术上的考虑,例如内存开销和更大的二进制文件外,还有逻辑上的陷阱,即使是经验丰富的程序员也无法免疫。

Linus 的观点是,错误应用的类层次结构和设计最终会导致不可维护的代码。在 OOP 中,创建臃肿的类层次结构并不少见。尽管内核开发人员肯定不会在大规模上犯这样的错误,但在小规模上可能会。而在一个像 Linux 内核这样庞大且复杂的代码库中,这种情况同样危险。

无论你的代码在技术上有多好,如果你在逻辑上搞乱了类层次结构、结构和设计,你就可能陷入维护的死胡同。这种情况下,可能就需要对类结构进行彻底重构。事实上,OOP 应用程序在短短几年后就需要对应用架构进行完全重构的情况并不少见。如果使用了 C++,Linux 内核也有可能会走上这条道路——这种风险真的值得吗?

(6)你最终还是会用回纯 C

“换句话说,唯一能写出高效、系统级且可移植的 C++ 代码的方法,就是限制自己只使用那些基本上在 C 中可用的东西。”

上述提到的特性都不是 C++ 所必需或强制要求的,你完全可以像写 C 语言代码一样去编写 C++ 代码。Linus 的观点是:既然如此,那为什么还要用 C++ 呢?如果你用 C 语言的风格写 C++ 代码,,那么 C++ 编译器就变得毫无意义了。

“将项目限制在 C 语言中,意味着人们不会搞砸,也意味着你会吸引到许多真正理解底层问题的程序员,他们不会因为愚蠢的‘对象模型’而搞砸事情。”

这是他在 2007 年 9 月 6 日的邮件列表帖子最后一段中提到的第二个观点。这句话的核心意思是,C 语言会吸引那些专注于硬件并解决实际问题的内核开发者,他们不会迷失在面向对象编程的外表和混乱中。

毕竟,开发者也是人,而这个观点强调了人在编程中的重要性。它表明,作为 Linux 世界的“独裁者”,Linus 不仅要考虑技术问题,还要像其他领导者一样管理人。他是目前全球最大开发者社区之一的教父,应该继续保持这种状态。某种程度上来说,他必须应对和管理全球超过 2000 万开发者的期望、希望和梦想。

(7)下一步是什么,Rust、Go 还是甚至原生 Java?

“C 语言最终是一种非常简单的语言,这也是我喜欢 C 语言、以及许多 C 程序员喜欢 C 语言的原因之一。然而,也正是因为其简单性,它也容易出错,而 Rust 就不会。” ——源自 Linus Torvalds 与 Dirk Hohndel 的一次对话

这里顺便提一下,已经有关于在 Linux 内核中使用 Rust 的讨论了。下一步是什么,Go、C#,还是通过 GraalVM 编译成原生二进制文件的 Java?部分开发者会提出这些问题,就像有些人提出 C++ 一样,这并非出于恶意。

Linus 必须在某个地方划清界限。Linux 内核 99% 是纯 C 语言,剩下的 1% 是纯汇编语言。内核的架构是人类历史上最干净的操作系统内核架构。而内核面临的挑战不仅仅是编程挑战,还包括中断处理、上下文切换和其他低级 CPU 操作等技术挑战。

内核需要提供设备驱动程序、文件系统、Linux 出色的网络堆栈、内存管理、进程调度以及更多功能。这些功能都具有很高的技术复杂性,添加任何与这些挑战无关、且不能直接为用户带来好处的复杂性,都是非常值得怀疑的。在 Linux 内核中引入 Rust 代码,比引入 C++ 会带来更多的复杂性,因此许多人对 Rust 引入内核这件事的看法是“不”。

(8)拒绝 C++ 的理由是否合理?

视情况而定,可以说是“是”也可以说是“否”。Linus 提出的论点在用户级应用程序(如绘图应用程序)的背景下可能是可笑的,但在内核开发的背景下,这些观点就是非常重要且合理的,因为它们突出了内核开发的严肃性。

Linux 的成功源于性能和稳定性,这得益于其 99% 纯 C 代码库和 1% 汇编代码的高度优化。引入 C++、异常处理、面向对象编程或任何其他未经测试的新奇技术,必然会对其产生负面影响。虽说把 C++ 形容为新奇技术或未经过测试的技术听起来有些荒谬,但对 Linux 内核来说确实如此。


图片

我们可以从 Linus 对 C++ 的拒绝中学到什么?

Linux 内核是全球独一无二的软件工程奇迹。超过 3000 万行代码运行在全球数十亿台机器上,其性能和稳定性无与伦比,这是内核开发者专注和奉献的直接结果。关于 systemd 与 SysVinit 的持续争论表明,开发者对 Linux 的重大变更既十分重视,又考虑到各种技术细节。

Node.js 应用中常见的“依赖地狱”与精心编写的 3000 万行 Linux 内核代码形成了鲜明对比。我们从 Linux 内核关于 C++ 的争论中可以学到的关键教训是:我们应该对依赖关系做出非常明智和理性的决策,包括考虑长期影响。

此外,Linus 对待 C++ 甚至 Rust 内核开发的态度表明,“使用合适的工具来完成工作”是多么重要。将一切都标准化为纯 C 已经使 Linux 内核标准化,但它仍然充满着内核开发者每天努力解决的挑战。当你的同事明天想用 Python 实现你 Go 应用的一个功能时,你可以考虑一下 Linus 的方法。


图片

结语

下次当你被推进医院的 MRI 扫描仪时,问问自己是否希望那东西能够抛出异常。当“智能冰箱”变得有自我意识并吃掉你的零食时,你可能会觉得有趣,但如果 MRI 扫描仪也自行其是,那可能会致命。

如今 Linux 内核无处不在,所有智能设备,包括计算机、手机、服务器、网络设备、嵌入式系统、游戏机、医疗设备、家用电器、可穿戴设备、汽车、交通信号灯、工业机械、航天系统、铁路控制系统、ATM、空中交通管制系统、卫星、科学仪器、医疗设备、农业机械、太空望远镜、潜艇、战斗机和巡航导弹……这些只是其中的一部分,你愿意冒风险去破坏它们吗?

虽然 Linus 显然厌倦了关于内核开发中 C++ 的问题,但提出并审视这个问题是很重要的。尽管答案是“不”,但我们可以从这个答案中学到一些非常重要的东西:这是花哨的还是必要的?我这样做是为了自己还是为了我的用户?这样做会产生什么样的长期影响?

原文链接:https://medium.com/@jankammerath/linus-torvalds-critique-of-c-a-comprehensive-review-ea8374084abf


推荐阅读  点击标题可跳转

1、C++训练营,来了!

2、HarmonyOS 学习资料分享(无套路免费分享)

我组建了一些社群一起交流,群里有大牛也有小白,如果你有意可以一起进群交流。

欢迎你添加我的微信,我拉你进技术交流群。此外,我也会经常在微信上分享一些计算机学习经验以及工作体验,还有一些内推机会

加个微信,打开另一扇窗

感谢你的分享,点赞,在看三  


C语言与CPP编程 C语言/C++开发,C语言/C++基础知识,C语言/C++学习路线,C语言/C++进阶,数据结构;算法;python;计算机基础等
评论 (0)
  • 飞凌嵌入式作为龙芯合作伙伴,隆重推出FET-2K0300i-S全国产自主可控工业级核心板!FET-2K0300i-S核心板基于龙芯2K0300i工业级处理器开发设计,集成1个64位LA264处理器,主频1GHz,提供高效的计算能力;支持硬件ECC;2K0300i还具备丰富的连接接口USB、SDIO、UART、SPI、CAN-FD、Ethernet、ADC等一应俱全,龙芯2K0300i支持四路CAN-FD接口,具备良好的可靠性、实时性和灵活性,可满足用户多路CAN需求。除性价比超高的国产处理器外,
    飞凌嵌入式 2025-05-07 11:54 98浏览
  • 后摄像头是长这个样子,如下图。5孔(D-,D+,5V,12V,GND),说的是连接线的个数,如下图。4LED,+12V驱动4颗LED灯珠,给摄像头补光用的,如下图。打开后盖,发现里面有透明白胶(防水)和白色硬胶(固定),用合适的工具,清理其中的胶状物。BOT层,AN3860,Panasonic Semiconductor (松下电器)制造的,Cylinder Motor Driver IC for Video Camera,如下图。TOP层,感光芯片和广角聚焦镜头组合,如下图。感光芯片,看着是玻
    liweicheng 2025-05-07 23:55 471浏览
  • Matter协议是一个由Amazon Alexa、Apple HomeKit、Google Home和Samsung SmartThings等全球科技巨头与CSA联盟共同制定的开放性标准,它就像一份“共生契约”,能让原本相互独立的家居生态在应用层上握手共存,同时它并非另起炉灶,而是以IP(互联网协议)为基础框架,将不同通信协议下的家居设备统一到同一套“语义规则”之下。作为应用层上的互通标准,Matter协议正在重新定义智能家居行业的运行逻辑,它不仅能向下屏蔽家居设备制造商的生态和系统,让设备、平
    华普微HOPERF 2025-05-08 11:40 399浏览
  • 文/郭楚妤编辑/cc孙聪颖‍相较于一众措辞谨慎、毫无掌舵者个人风格的上市公司财报,利亚德的财报显得尤为另类。利亚德光电集团成立于1995年,是一家以LED显示、液晶显示产品设计、生产、销售及服务为主业的高新技术企业。自2016年年报起,无论业绩优劣,董事长李军每年都会在财报末尾附上一首七言打油诗,抒发其对公司当年业绩的感悟。从“三年翻番顺大势”“智能显示我第一”“披荆斩棘幸从容”等词句中,不难窥见李军的雄心壮志。2012年,利亚德(300296.SZ)在深交所创业板上市。成立以来,该公司在细分领
    华尔街科技眼 2025-05-07 19:25 454浏览
  • 硅二极管温度传感器是一种基于硅半导体材料特性的测温装置,其核心原理是利用硅二极管的电学参数(如正向压降或电阻)随温度变化的特性实现温度检测。以下是其工作原理、技术特点及典型应用:一、工作原理1、‌PN结温度特性‌硅二极管由PN结构成,当温度变化时,其正向电压 VF与温度呈线性负相关关系。例如,温度每升高1℃,VF约下降2 mV。2、‌电压—温度关系‌通过jing确测量正向电压的微小变化,可推算出环境温度值。部分型号(如SI410)在宽温域内(如1.4 K至475 K)仍能保持高线性度。
    锦正茂科技 2025-05-09 13:52 263浏览
  • 随着智能驾驶时代到来,汽车正转变为移动计算平台。车载AI技术对存储器提出新挑战:既要高性能,又需低功耗和车规级可靠性。贞光科技代理的紫光国芯车规级LPDDR4存储器,以其卓越性能成为国产芯片产业链中的关键一环,为智能汽车提供坚实的"记忆力"支持。作为官方授权代理商,贞光科技通过专业技术团队和完善供应链,让这款国产存储器更好地服务国内汽车厂商。本文将探讨车载AI算力需求现状及贞光科技如何通过紫光国芯LPDDR4产品满足市场需求。 车载AI算力需求激增的背景与挑战智能驾驶推动算力需求爆发式
    贞光科技 2025-05-07 16:54 230浏览
  • UNISOC Miracle Gaming奇迹手游引擎亮点:• 高帧稳帧:支持《王者荣耀》等主流手游90帧高画质模式,连续丢帧率最高降低85%;• 丝滑操控:游戏冷启动速度提升50%,《和平精英》开镜开枪操作延迟降低80%;• 极速网络:专属游戏网络引擎,使《王者荣耀》平均延迟降低80%;• 智感语音:与腾讯GVoice联合,弱网环境仍能保持清晰通话;• 超高画质:游戏画质增强、超级HDR画质、游戏超分技术,优化游戏视效。全球手游市场规模日益壮大,游戏玩家对极致体验的追求愈发苛刻。紫光展锐全新U
    紫光展锐 2025-05-07 17:07 345浏览
  • 这款无线入耳式蓝牙耳机是长这个样子的,如下图。侧面特写,如下图。充电接口来个特写,用的是卡座卡在PCB板子上的,上下夹紧PCB的正负极,如下图。撬开耳机喇叭盖子,如下图。精致的喇叭(HY),如下图。喇叭是由电学产生声学的,具体结构如下图。电池包(AFS 451012  21 12),用黄色耐高温胶带进行包裹(安规需求),加强隔离绝缘的,如下图。451012是电池包的型号,聚合物锂电池+3.7V 35mAh,详细如下图。电路板是怎么拿出来的呢,剪断喇叭和电池包的连接线,底部抽出PCB板子
    liweicheng 2025-05-06 22:58 646浏览
  • 温度传感器的工作原理依据其类型可分为以下几种主要形式:一、热电阻温度传感器利用金属或半导体材料的电阻值随温度变化的特性实现测温:l ‌金属热电阻‌(如铂电阻 Pt100、Pt1000):高温下电阻值呈线性增长,稳定性高,适用于工业精密测温。l ‌热敏电阻‌(NTC/PTC):NTC 热敏电阻阻值随温度升高而下降,PTC 则相反;灵敏度高但线性范围较窄,常用于电子设备温控。二、热电偶传感器基于‌塞贝克效应‌(Seebeck effect):两种不同
    锦正茂科技 2025-05-09 13:31 247浏览
  • 二位半 5线数码管的驱动方法这个2位半的7段数码管只用5个管脚驱动。如果用常规的7段+共阳/阴则需要用10个管脚。如果把每个段看成独立的灯。5个管脚来点亮,任选其中一个作为COM端时,另外4条线可以单独各控制一个灯。所以实际上最多能驱动5*4 = 20个段。但是这里会有一个小问题。如果想点亮B1,可以让第3条线(P3)置高,P4 置低,其它阳极连P3的灯对应阴极P2 P1都应置高,此时会发现C1也会点亮。实际操作时,可以把COM端线P3设置为PP输出,其它线为OD输出。就可以单独控制了。实际的驱
    southcreek 2025-05-07 15:06 564浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦