1.1 类型的作用
C语言解决了B语言[1]存在的一个严重的问题——类型问题。最初B语言是按字长取址,其运行机器也比较简单,那时候语言的唯一数据类型称为word或者cell。当时还没有类型的概念,只是存储数据的一个单位罢了。直到PDP-11计算机的出现暴露了B语言模型的不足之处。
首先,它不适合处理单字节。需要将字节打包到cell上,而且读写时涉及重组这些cell;其次,PDP-11计算机支持浮点运算,B语言为了支持浮点运算引入特殊的操作符,而这些操作符是硬件相关的;最后是B语言对指针处理有额外的开销,指针作为数组的索引,需要运行时调整数组的下标才能被硬件所接受。从这些问题里可以看出类型系统的重要性,它可以让编译器生成正确指令以及对应数据的存储方式。
后来类型系统的重要性更多体现在类型安全、类型检查上。类型不仅能够给数据赋予意义,还能充当接口,对行为进行约束。做一个让大量的猴子编写程序的思想实验[2],每个猴子每次随机敲下键盘的按键,然后编译、运行。
如果这些猴子采用的是机器语言,那么每个字节的组合都可能被图灵机[3]解释并执行,只不过这样的执行结果将毫无意义。而高级语言(例如C++语言)有自己的词法、语法规则,输入的字节组合能够被编译器检查,那么凭借这样的功能在运行前能拦住很多无意义的字节组合,最终可运行的程序或多或少都有意义[4]。例如定义了一个“人”的类型,那么其对象在运行过程中不会变成狗,也不会飞。
这里的类型检查就能阻止很多无意义的程序[2]。与动态类型语言相比,在运行时所检查出的因类型而引发的错误都能够在编译时发现,这能节省很多程序调试的时间。软件开发过程中往往涉及重构,这些重构可以在保证功能不变的情况下,使得软件的可维护性更好。静态类型语言在重构后能及时发现类型错误,例如通过重构函数的形参类型,在编译时便能找出所有调用者,从而避免了遗漏。有些观点是在动态类型语言中通过添加大量测试来避免了因为类型问题而导致的错误,而测试本身往往是非确定性的,是个证伪的过程。当然仅通过类型系统是不可能完全保证程序的正确性的,但至少在一定程度上保证程序的正确性。