注册

透过Redis看待我们是否应该继续使用c语言

杰克-逊の黑豹,恰饭了啦 []( ̄▽ ̄)


keywords: Redis c cpp


厌弃c语言的现象


长久以来,程序员们对待c语言的态度非常矛盾,主要应该有这么几种:



  • 喜爱、工作中使用c语言;
  • 不反感、工作中使用c语言;
  • 不反感、工作中不使用c语言;
  • 反感、工作中使用c语言;
  • 反感、碰都不碰c语言;

赞许c语言的程序员,可能会有这些观点:



  • 使用c语言的程序员是最nb的;
  • 真正的编程大佬都使用c语言;

讨厌c语言的程序员,大抵是因为这些:



  • 没有舒服的包管理机制;
  • 没有丰富的标准库;
  • 没有面向对象的语料支持;
  • 内存不够安全(内存泄漏、悬垂指针、非法指针、无效指针等等);
  • 编译器输出信息不够好;
  • 错误处理不够舒服;
  • 项目管理复杂;

再加上,像Rust/Go/Swift等这种现代编程语言的出现,更让很多程序员厌弃c语言。



现代编程语言和c语言比起来,有什么最大的特点?以我个人而言,最大的特点就是编程语言的表达能力
非常出色,不是C语言可以比拟的。表达能力更具体地讲就是更灵活、更模块化、更适合人阅读。



难道说c语言真的是魔鬼吗?


反驳者一定会举出嵌入式开发、操作系统开发、驱动开发领域的成功案例。


但我觉得,这样就又大又空洞了。


近日,我在看Redis1.3.6源码,觉得倒是个很好的例子说说这事儿,不过读者大可放心,本文不是xx源码阅读, xx源码细品的文章,更不是什么八股文,单纯作为一个例子,解释下c语言是不是魔鬼这回事儿。


先说下我个人的结论吧。


c语言是不是魔鬼,取决于你能hold住多少c语言代码量。hold住,c语言的缺点不是事儿;hold不住,c语言就会带来悲剧。一定程度上,这就像我们尝试三个小时保持精神高度集中一样,诚然你说自己的c语言功夫非常扎实,但是随着项目规模增大,我可不相信你照样可以注意到每个角落的c语言隐患。


如果你是95后,c语言不适合作为你的主力开发语言,但是你可以学习由c语言开发且非常稳定的项目,也可以基于c语言ABI将这些c语言项目植入到别的编程语言开发的项目中,比如Rust/Go/Javascript/Swift项目。


为什么选择Redis1.3.6


事物总是由简入繁,功能总是由少渐多,项目总是由骨到肉。


越早的版本,越容易看出作者的宏观思路。


越新的版本,越会充斥各种新功能、小功能,越发遮掩作者最初的构思。


我从github下载好Redis后,发现最早的一个版本号就是1.3.6, 所以就用这个做参考了。



实际上,我硬着头皮看了两天最新的版本,发现确实啃不动。



看看Redis怎么解决包管理问题的


在1.3.6版本中,redis没有依赖的包,但是在最新版本中,redis将这些依赖放置于deps文件夹下。这里边没有什么特别的地方,就是将依赖的仓库源码放置在一个单独的文件夹下,定期去看看仓库源码有没有更新,如果更新的话,按需要将本地做更新,就和你的本地仓库、远程仓库一样。
截屏2023-05-23 23.29.23.png


同时,你也看到了,依赖并不多确实可以这样做,如果依赖多的话,这么搞就难搞了,就需要包管理程序(第三方的或者自己开发的)。不过嘛,一般用c语言开发的项目,都是追求极度性能,功能非常专一的项目,不会掺入太多的依赖。但是应用层项目就不同了,依赖贼多。


看看Redis怎么解决标准库不太丰富的


c语言开发时用到的API基本上是操作系统原生提供的API,这些API都分布在操作系统提供的固定的.h文件中,比如:



  • unistd.h
  • sys/sysctl.h
  • pthread.h
  • sys/stat.h
  • fcntl.h
  • execinfo.h
  • ucontext.h

当然也需要c语言提供的标准库.h文件,比如:



  • stdlib.h
  • stdio.h
  • stdarg.h
  • erron.h
  • ctype.h

例如redis.c文件中就用到了很多耳熟能详的.h文件:


截屏2023-05-24 00.19.15.png


两类之外的功能,要么从第三方拷贝,要么就要自己写。当然,在redis1.3.6中依赖并不多,只需要一些数据结构的实现,比如哈希表(hash table)、动态字符串(dynamic string)、双向链表(double linked list)、压缩型字符串映射表(zip map, 应该是redis作者独创的)。这些都是redis作者实现的。


可不要被实现吓坏了。


自己创造式地想到一个数据结构,然后用代码写出来,叫做实现;


自己参考数据结构书籍、论文中的理论,给出自己的实现,这也叫做实现。


都成年人了,要知道:考试不是只叫做闭卷考试


像pqsort(部分快速排序),redis作者就是参考NetBSD平台下的libc源码实现的,作者在代码注释中给出了声明:


截屏2023-05-24 00.16.58.png


你可能会问了,我实现的版本性能无法保证怎么办?redis作者其实也告诉我们答案了,那就是先搞出一版实现,至于性能优劣是另一个问题,可以先不用管它。于是,在ae.c的文件中,我们看到redis作者写到这样的注释:


截屏2023-05-24 00.24.19.png


c项目一般保持功能专一、性能卓越,常常要定制化一些数据结构的代码,所以即便标准库加入这些数据结构,也未必能满足c项目的需要,可能也派不上多大用场。


如此看来,c项目不太需要那种普适、统一的标准库,更需团队开发、企业开发、组织开发范围内的标准库。


但是对于技术经验尚浅(没查阅论文、手册、其余资料,独立实现一些功能库)的开发者来说,他们需要的是涵盖功能的标准库,而不是刚提到的那种高度定制化的标准库。


所以标准库不太丰富的这种问题,对于项目不大、开发者具备经验的情况而言,不成问题;对于经验不足的开发而言,是个头大的问题;对于项目贼大的情况而言,无论经验多少,都是头大的问题。


看看Redis是怎么解决面向对象编程的


面向对象编程,说的就是一种编程思想,但一部分开发者常常会被编程语言的形式所蒙蔽,认为编程语言给出了面向对象代码形式(提供class extends public等关键字),才算面向对象了,对于没有给出这种形式的编程语言,就无法面向对象编程。


看看Redis是怎么做的吧。


截屏2023-05-24 00.41.58.png


struct等效于classaeBeforeSleepProc就是类中定义的成员方法啊,其定义如下:


截屏2023-05-24 00.43.17.png


至于继承,可以使用组合的方式替代;


至于多态,可以继续使用函数指针或者改用函数映射表替代;


宽泛地讲,只要有了封装, 即便没有给出严格的继承多态, 也可以认为是面向对象。毕竟,面向对象是一种编程思想,不是僵死的格式。


不过呢,语言本身如果提供了面向对象概念的关键字,代码直接理解起来的难度就大大降低,加之有IDE功能的帮助,读代码更加顺畅,这是c代码所不及的。这也就是说,当项目的概念变得非常多,c代码即便可以面向对象编程,但也是个问题,会让读代码的人一通找啊找。


Redis怎么解决内存安全问题的


这个问题确实是要害,在这个版本中,redis并没有给出什么非常好的内存安全手段,有的话,也只是在定义数据结构的时候,给出该数据结构的内存释放方法:


截屏2023-05-24 00.54.20.png


和cpp的析构函数一个道理,只不过cpp是编译器插入内存释放代码,c语言是开发者手动加入。


在代码量hold的住的情况下,开发者确实有足够的精力和耐心确保内存问题,但是代码量一旦上涨,开发者恐怕就顾不过来了。你可以看到像linux这样大型的项目有不少issue,但你很少听说hello-world级别的c代码有什么issue。


所以,到底是人去处理内存问题还是编译器处理内存问题,归根结底就是在大量工作的条件下,你更相信人的表现还是机器的表现。相比之下,我觉得机器更靠谱一些。


c项目很复杂,无法阅读?


一般而言,在同样成熟的情况下,c项目的代码读起来会麻烦一些。但这是成熟之后,也就是在加入杂七杂八功能、BUG布丁等等之后的c项目。以Redis 1.3.6为例,其结构相当简洁:


截屏2023-05-24 01.02.53.png


主体框架就这么直白,后续版本的复杂化,都是功能扩展、结构调整,这条主线是不可能断掉的。所以,别把C项目想象的那么复杂,那么难。C项目的复杂还有一种可能是C语言表达能力导致的。因为C语言语法足够简洁,直接用的操作系统API,封装比较少,别的语言三言两语交代的事情,C语言可能要多说很多话才可以表述出来,但是编译到了二进制形式的产物,C语言就比别的语言“简洁”了。


后话


C语言并没有那么可怕,它不是开发者的定时炸弹,也不是开发者的无双宝剑。C语言到底怎么样,很大程度上要看代码量和开发者的承受能力。并不是说很多项目由C语言编写,就证明C语言是一门你可以信赖并使用的语言,很大程度上讲,很多项目用C语言开发是历史问题,在那个年代和阶段,编程语言的选择余地太少,哼不能从1980年等到2010年以后,再使用现代化编程语言编写代码吧?


从另一个角度上看,这个时代你完全可以不使用C语言,没必要跟着上古大佬的品味走。须知,工具是用来解放你的生产力的,不是给你的生产力添堵的。你用不用C语言,和你是不是一个合格的软件工程师,没什么必要的联系。


不过,不用归不用,学习还是要学习的,毕竟ABI层面还是以C语言为标准的,想理解函数调用、指令跳转等内容,C语言是避不开的。


对于要不要继续使用C语言,你有什么看法呢,欢迎留言。


作者:杰克逊的黑豹
来源:juejin.cn/post/7236354905493176377

0 个评论

要回复文章请先登录注册