【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

今时今日,C 还适合当下之所需么?

  • 2013-01-27
  • 本文字数:4636 字

    阅读完需:约 15 分钟

来自 Couchbase 的 Damien Katz 认为 C 依然是非常适合于后端编程的一门语言,然而有的开发者则觉得 C 有太多的瑕疵,他们支持 C++ 或是 Java,还有一些人连这两种语言也不喜欢。

在最近一篇题为 The Unreasonable Effectiveness of C 的博文中, CouchDB 的创建者 Damien Katz 表示 C 依然是非常适合于后端编程的一门语言,虽然现在已经有了很多更加现代化的语言,如 C++、Java、甚至是 Erlang 或是 Ruby,但他还是非常支持 C 语言。Katz 并不是认为 C 就是比其他任何语言都要好,但在“重点考虑性能与可靠性等场景下,C 是很难被打败的”——这段话援引自 Damien Katz 随后的一个帖子,旨在澄清自己的立场

虽然一开始使用 Erlang 编写了 CouchDB 的很多代码,但在花费了“2+ 个人月来处理 Erlang VM 中的一个崩溃问题”之后,Katz 感到非常不爽。

我们浪费了大量时间追踪核心 Erlang 实现中的一些问题,不敢保证发生什么以及原因,我们觉得也许问题出现在我们自己的插件 C 代码中,希望我们自己能够发现并修复问题。但事实并非如此,这是核心 Erlang 中的一个竞态条件 Bug。我们只能通过 Erlang 的代码查看器来找到这一问题。对于那些对计算机进行了过多抽象的编程语言来说,这是个很基本的问题。

出于这一点以及性能因素,Katz 决定逐步重写,“将 Couchbase 的代码换成 C,并将其作为大多数新特性的首选实现语言”。有趣的是,C 证明了“当我们遇到问题、调试与修复问题时,C 更具备可预测性。长远来看,C 的生产力更高”。

Katz 列出了对于后端来说,C 要优于更高层次语言如 C++、Java 等的若干原因:

  • 表现力——“C 的语法与语义非常强大且具有极强的表现力。凭借 C,我们既能预测高层算法,又能预测低层的硬件。它的语义非常简单,语法足够强大,能够极大降低认知上的负担,让程序员专注在重要的事情上”。
  • 简单——“C 是一种弱、静态类型语言,其类型系统非常简单。我们所说的弱最后会变成一个优点:C APIs 的“表面”是非常简单且小巧的。相对于大多数框架来说,C 的一个明显趋势与文化就是创建小型的库,对简单类型进行轻量级的抽象”。
  • 速度与内存使用——“C 是速度最快的语言,无论是部分还是完整的基准都表明了这一点。它不仅仅运行时是最快的,其内存使用与启动时间也是效率最高的。如果需要在空间与时间上进行折衷,那么 C 并不会对你隐藏任何细节信息,我们可以很容易地做出估计”。
  • 更快的开发周期——“对于开发者效率与生产力来说最为重要的就是‘构建、运行与调试’周期。周期越快,开发的交互性就越好,你就更容易处在任务的流态上。相对于所有主流的静态语言来说,C 拥有最为快速的开发交互性”。
  • 调试——“对于纯 C 代码来说,你可以查看调用堆栈、变量、参数、线程局部变量、全局变量,基本上可以查看到内存中的一切。这是非常有用的,特别是当你遇到了问题,这个问题在运行的服务器进程中出现了多日,并且无法重现的情况下更是如此。如果在更为高层的语言中失去了这个上下文,那么你就等着痛苦去吧”。
  • 跨平台——“有一个标准化的应用二进制接口(ABI),可为现有的所有操作系统、语言与平台所支持。它无需运行时,也不需要其他额外内容。这意味着你使用 C 所编写的代码并非仅仅可由 C 代码中的调用者所调用,还可以由现有的所有库、语言与环境所调用”。

Katz 也认为 C 有“很多瑕疵”:

没有范围检查,不小心就会导致内存出现问题、存在野指针与内存 / 资源泄露情况、对并发的附加支持、没有模块、没有命名空间。错误处理非常麻烦且冗长。很容易就会搞出一堆错误,调用堆栈也找不到了,恶意输入会控制你的进程。闭包?哈哈

Katz 对 C 的强烈偏爱源自突破 Couchbase 性能极限的需求以及调试问题(问题是 C 插件与 Erlang VM 联合使用所导致的)。他并不认为 C++、Go 或是 D 能够替换 C,但他认为 Rust 可能会成为“梦想之语言”,只要它能实现“类似于 C 的性能而且能够做到与 Erlang 安全的并发和内建的健壮性”。

Katz 的帖子在 Reddit Hacker News 上可谓是一石激起千层浪,有很多开发者谈到了 C 的优点,也有人建议其他语言。 robinei 加入到了字符串操作与错误检测激战当中:

我总想回到 C(从 C++ 等语言中),当我真的这么做了时,我发现不少地方通常都是很简单的,感觉真棒! 但接下来我需要进行字符串操作,或是这类笨拙的方法。

这时会出现很多分配操作,每一个都需要一个显式的 free 搞得我太痛苦了。我尝试通过 arena allocators 树来解决这个问题,就像 Go 中的 slice-strings,但最终 C 还是缺乏一些语法工具(命名空间前缀函数),这导致结果变得非常笨拙(将一切都分配到 arenas 中也是非常痛苦的)。

我发现由于要进行显式的错误检测,源代码文件长度增加了一倍(这种情况不常发生,但在诸如 sqlite 等一些库中,任何操作都有可能失败)。

还有很多方面导致我精疲力竭,我觉得非常不满意。

综上所述,我从哪儿来的还是回哪儿去吧,通常是 C++。

madhadron 提出了“更加现实的 C ”:

C 能够在 PDP-11 上很直接地编译成快速的机器码。 C 的标准库就是个笑话。它的缺点,特别是字符串相关的处理,是过去 40 年众多安全漏洞的罪魁祸首。

C 的工具根本不值得吹嘘,特别是与同辈的 Smalltalk 和 Lisp 相比。人们所使用的大多数 C 调试器都是命令行。根本没法和 Squeak 或是 Allegro Common Lisp 的标准调试器相比。

声明快速的 C 构建 / 调试 / 运行周期令人沮丧。表面上看起来很快,因为 C++ 在这个领域是失败的。如果你想知道如何加快构建 / 调试 / 运行周期,那么请看看 Turbo Pascal 吧。

你可以通过标准 ABI 在所有 Unix 上调用 C,虽然这么说没错,但原因却是因为 C 的普遍存在性而已。

geophile 对上述内容持不同看法

C/C++/Java,这是程序员视角的石头 / 剪刀 / 布。 多年以前,我从 C 开始,我发现自己用宏和库为声明与函数提供了很多很有用的组合。我发明了对象,同时也发现了 C++。

长久以来,我一直是个快乐的 C++ 用户,很早就开始了(还是 cfront 时代)。但我被语言的复杂性搞崩溃了,特别是特性之间微妙的交互,我厌倦了内存管理,渴望 Java,而它又适时地出现了。

我很开心。在学习语言时,我肯定我漏掉了某些东西。每个对象都在堆中么?真是如此么?真的没有办法将一个对象在物理上嵌入到另一个对象中么?但其他一切都很棒,我不介意这一点。

现在我在编写一些系统,这些系统会占用很多内存,包含成千上万的对象,有些对象很小,有些则很大。每个对象的代价快要搞死我了。GC 调优是个梦魇,我正在实现子分配模式。我编写了微基准,比较普通对象与序列化为字节数组的对象。由于 C++ 已经变得太恐怖了,比令我焦头烂额的早期版本还要复杂,因此我又渴望 C 了。

现在的我不再喜欢任何语言了。

对于某些人来说,C 看起来瑕疵太多,已经不适应现代的生产力要求了,但还有不少人依然能够很好地使用 C,尽管它有很多怪癖。开发者社区还是应该避免语言之争,而是更好地权衡每一种语言,根据项目需求与自身技能选择最合适的语言。毕竟,没有一种语言是完美的。

此文在 InfoQ 英文站也引来了众多读者的讨论,下面摘取部分读者的评论供大家参考。

读者 Mark Peskin 说到:

嗯,我喜欢 C。它很简单,没有 C++ 那些庞大且有瑕疵的面向对象特性。然而,使用 C 编写大型、可扩展、模块化的企业应用需要大量规则,这些规则在大型的软件企业中几乎是无法维护的。我认为 C 的一个问题是它会引诱聪明的开发者编写高度优化的代码,充满了 memcpy() 与指针运算,以此“打败编译器”,这对于其他开发者来说几乎无法理解(如果不信,你去读读 BSD 内核代码吧)。 总的来说,如果有很多开发者在开发大型、复杂且长期的项目,那么我认为你最好使用 Java(或是 Scala 等语言)。将 C 用在那些偶尔出现的场景中吧,这时你可能真的需要本地代码,使用基于消息的系统将二者集成起来(JNI 不行)。C++?还是算了吧。

读者 Josh Long 说到:

回应 Katz 关于 C 的问题。 我同意 Mark 的观点,在某种程度上,也认可 Damien 的看法。

Damien 提到的一点是我们很少在 C 中看到真正大型、全面的框架,比如 APIs。如果构建小型、某个方面的 API(通过一些 typedefs/structs 与函数作为“契约”),那么我们可以很轻松地将库“导出”并重用。我觉得这是 C 最适合之处。我从来不会因为性能问题而使用 C,但使用 C 实现某些功能则是更为轻松的事情(内核编程、嵌入式编程、处理硬件、所依赖的 APIs 并未在更高层次的语言如 Java、Ruby、Python 等进行过抽象的功能)。

我还尽量不使用 C 编写具有完整功能的系统,只是因为它对于大型项目来说不具备“可伸缩性”。使用 C 编写的大型项目最终的结果都是重新编写了很多东西(比如说对象与命名空间等)。恕我直言,真的没有多少领域需要从头到尾都用 C 不可。当然了,一些例外是系统级组件,比如说操作系统(Linux)或是 UI,如 GNOME。但对于应用来说,使用更高层次的语言,在高层次语言与平台之间存在缝隙之处再使用低层次 APIs 进行集成是更容易的做法。Java 存在很多这类“缝隙”,但随着 APIs 逐步成为很多不同操作系统上的常客后,在过去 10 年间,有些已经被逐步解决了:事件驱动的 IO、文件系统通知、文件权限与元数据等等。

Mark 为集成 C 库与模块提出了很好的解决方案。他认为 JNI 不行,建议使用消息。我是消息的忠实粉丝。本质来说,成功使用消息与成功使用 JNI 都需要同样的东西:你需要彻底简化导出 API。

在使用 JNI 时,绝不要将任何复杂的 C 类型“泄漏”到我的 Java API 中,反之亦然,并且总是通过数值类型与 char* -> jstrings 进行通信。即便我所公开的本地代码是用 C++ 编写的,我也依然会使用 C 风格的 JNI(而不是 C++),因为这种规范化有利于互操作。如果保持 C API 表面的简洁性,并且避免线程,那么通过 Java JNI、CPython 或是 MRI Ruby 等可以将其作为本地扩展。

一旦完成了这个过程,接下来通过消息公开 C API 就变得很简单了,因为根据定义,两个系统之间的消息负载不可能比 C 库还要复杂。当然了,如果使用消息,这意味着要么使用 C 编写消息代码,要么将 C 库公开到更高层的语言上,并在那里实现消息。消息好的一面是能够将高层语言代码与 C 代码进行隔离,这么做会比 Java 代码要薄一些。我依然不会直接将使用 C APIs 编写的代码链接到我的应用中。如果 C 代码挂掉了,那么消息系统就会接收请求,直到运行着 C 代码的另一个节点能够进行处理为止。另一方面,如果真的因为性能原因而使用 C,那么消息至少会引入一个网络传输,更不必说系统中的另一个组件了,这么做就抵消了使用 C 编码所带来的优势了。在这种情况下,我们可以编写稳定、行为良好的 JNI 或是本地扩展,但还是需要保持表面的小巧,并且理解前置与后置条件才行。没有线程。不要在 C 与 Java 之间传递指向复杂对象的指针。请确保你自己清楚谁来负责清理内存,什么时候清理。

总而言之,忘掉 C++ 吧。

读者 Bernd Kolb 说到:

或许你想要看看 mbeddr( mbeddr.com )。 mbeddr 旨在更好地支持嵌入式软件开发(但并不仅仅限于此),针对基于 C 语言与 IDE 的可扩展版本的小型与大型系统。现有扩展包括前置和后置的接口、组件、状态机与物理单元,以及对需求追踪和产品线变化的支持。基于这些抽象,mbeddr 还支持基于模型检测与 SMT 处理的形式验证。

通过这种方式,在大型项目与团队中,C 也可以做到“可伸缩”。此外,我们还可以引入现代的编程规则。通过扩展 mbeddr,你甚至可以为消息等添加基础信息。

查看英文原文: Is C Still A Suitable Language Today?

2013-01-27 05:554858
用户头像

发布了 88 篇内容, 共 258.5 次阅读, 收获喜欢 8 次。

关注

评论

发布
暂无评论
发现更多内容

Android Zygote 从何而来?揭开Android系统启动的面纱,flutter真机调试

android 程序员 移动开发

Android 可能你想要的APK瘦身笔记,2021最新安卓大厂面试题来袭

android 程序员 移动开发

Android 启动优化: JetPack App Startup 使用及源码浅析

android 程序员 移动开发

Android 单元测试之 Mockk,idea开发android教程

android 程序员 移动开发

Android 常见的数据存储方式,腾讯T2大佬手把手教你

android 程序员 移动开发

Android 应用层开发 Drawable 的一些叨叨絮,跨平台移动开发答案

android 程序员 移动开发

模块二作业

ks

通过Rainbond的团队管理去管理已有的组织架构

北京好雨科技有限公司

最佳实践 多租户 开源软件 rainbond

Android 使用微信开放平台分享内容到微信,apm性能监控系统

android 程序员 移动开发

Android 存储空间的最佳实践,【金九银十】

android 程序员 移动开发

Android 实现水印背景效果,kotlin教程

android 程序员 移动开发

软件测试面试屡屡失败,面试官总是说逻辑思维混乱,怎么办?

六十七点五

学习方法 面试 软件测试 自动化测试 测试工程师

Android 应用层开发 Drawable 的一些叨叨絮(1),androidstudio中文社区

android 程序员 移动开发

CDP客户数据管理平台体系化搭建

CRM 客户数据平台 CDP DSP DMP

Android wifi属性简介 及 wifi信息获取(wifi列表、配置信息

android 程序员 移动开发

Android 可控制概率家务转盘,移动端跨平台开发大型项目

android 程序员 移动开发

使用桥接模式设计复杂的消息系统

Tom弹架构

Java 架构 设计模式

Android 实现双Launcher的无缝切换,flutter免费视频教程

android 程序员 移动开发

高风险IP究竟来自哪里?IP定位带你反欺诈

郑州埃文科技

Android WebView判定网页加载的错误,androidtv开发者模式

android 程序员 移动开发

Android 屏幕适配方案,安卓开发

android 程序员 移动开发

Android 开发市场是盛是衰?你应该知晓,android音视频开发面试题

android 程序员 移动开发

Android _《看完不忘系列》之Retrofit,flutter下载文件

android 程序员 移动开发

Android 使用Face++ SDK进行人脸识别和年龄检测,android开发网络框架

android 程序员 移动开发

Android 内存性能优化,灵魂一问-如何彻底防止APK反编译

android 程序员 移动开发

android 对不同日期和时间的格式方法的封装,46道面试题带你了解高级Android面试

android 程序员 移动开发

WordPress站点快速集成腾讯数字身份管控平台CIAM,免开发实现登录认证

腾讯安全

百度人脸活体检测系统通过信通院“护脸计划”首批优秀级安全防护能力评估

百度大脑

信通院 人脸活体检测

Android 四大组件全解读,kotlin极简教程

android 程序员 移动开发

Android 如何从应用深入到Framework (一),2018移动应用开发前景

android 程序员 移动开发

Android 存储进化:分区存储,2021最新Android笔试题及答案

android 程序员 移动开发

今时今日,C还适合当下之所需么?_Java_Abel Avram_InfoQ精选文章