C语言程序为什么比其他语言程序都要快?它是牺牲了什么换来的?
网络上似乎一直有种说法:C语言程序运行时要比其他语言编写的程序快得多,因为它“离底层机器很近”,这个说法正确吗?如果正确,那究竟是什么阻止了其他语言编写的程序和C语言程序一样快呢?
C语言程序快是因为它简单
编程语言其实就是程序员与机器沟通的一门“外语”,可以认为编程语言是为程序员和机器服务的。事实上,在设计编程语言时,常常需要在一些问题上取舍以寻求平衡,天平的两端则分别是程序员和机器。
人类和计算机的思考方式是有很大差异的,因此如果某种编程语言偏向程序员,那么可能程序员写程序会很方便,但是最终得到的程序对机器就不够友好了,效率会有损失。例如 Python,JavaScript 等脚本语言。
相反,如果某种编程语言偏向机器,那么最终得到的程序效率会得到很大程度的提升,但是这样的编程语言可能对于程序员就会不太友好,开发效率会有所降低。这类编程语言以C语言,以及汇编语言为代表。
C语言诞生时,计算机技术还不是很发达,这可能是影响“天平”平衡的一个重要因素。如今,新出现的一些编程语言通常都会更加“照顾”程序员,“垃圾回收”以及“动态类型”等机制几乎已经成为标配了。
原因也很简单,因为在如今快节奏(快到“浮躁”)的社会,开发效率低下的编程语言是无法得到广泛发展的。
正如前文所说,当编程语言的“天平”向程序员倾斜时,最终得到的程序效率自然会有所降低。因为编程语言要“照顾”程序员是要付出代价的——“垃圾回收”等机制本身也会消耗相当一部分的计算机性能。虽然今天的计算机技术已经大大发展,但是计算机的运算能力始终是有限的。
而C语言也没有这些额外的机制,自然最终C语言程序的运行速度也会比别的语言程序高。当然,这也意味着C语言程序员需要自己管理分配的内存,自己避免内存溢出、泄漏等问题,还要自己处理变量的类型。
再来谈谈C语言
设计人员在设计C语言时,更多考虑的是最终C程序的运行效率,因此像下面这样的几种安全检查,都要依赖程序员自己,C语言本身是不会检查的:
- 数组的索引边界
- 未初始化的变量值
- 内存是否泄漏
- 空指针的引用
以数组的应用为例,Java程序设计语言会在虚拟机中进行一些方法调用、绑定检查以及其他的一些安全检查。这是语言本身提供的服务,这些检查隐藏在底层,对开发应用的程序员是不可见的。但是这样的安全检查无疑对程序员是友好的,因为它增加了应用的安全性。
而在C语言程序开发中,即使是一些非常琐碎的事情也要程序员自己处理。例如在执行 memcpy() 等内存操作时,是不会检查要复制的内存区域是否有重叠的。
C语言的这些特性在有些程序员看来是缺陷,但其他一些程序员却认为这是一种灵活,能够让程序员具有更大的权限的管理机器,以及获得计算机的每一点性能。
虽然C语言号称是一种支持可移植程序开发的编程语言,它的一些语法也尽力实现这一目标,但是C语言并不想强迫程序员以可移植的方式编写代码,以防止C语言成为“高级汇编语言”,毕竟编写特定于机器的代码是C语言的优势之一。
C语言作为一门古老的编程语言,其热度却始终没有减少,自然的,C语言近些年也是得到很多发展和拓展的,从C89到C90,再到C99,C11标准。但是C语言始终没有偏离它的基本精神:
- 相信程序员,尽量把控制权交给程序员。
- 不阻止程序员做他想做的事,例如有时数组下标为负也允许 arr[-1]。
- 保持语言简洁。
- 只提供一种操作方法。
- 保持C语言程序的高效率,即使可能会与可移植性相悖。
最后一句需要稍加解释:生成高效的程序是C语言的最重要的优点之一。为了确保看似非常简单的操作不会导致崩溃,C语言有时宁愿在通用抽象规则上做出让步,这也是C语言标准中有许多“未定义”的规则。
例如,short int,int, long int 整数类型究竟占用多少内存空间,C语言标准并没有给出确定的定义,这就意味着这几种整数类型在不同的机器上占用内存空间大小可能是不同的。再比如,虽然C语言标准规定了 char 类型占用一字节内存空间,但是却没有定义其符号,也就是说 char 类型在有的机器上是有符号的,而在其他机器上可能是无符号的。
C语言的缺点
正如前文讨论的,C语言的“天平”更加偏向机器,这使得C语言程序员的工作量增加不少。有一些 Java 程序员甚至说:“C语言程序员花费一个月开发的程序运行需要 0.05 秒,而我只需要一天就能开发出这样的程序,它运行只需要 0.1 秒,所以,C语言快吗?”