环境

Last updated 2 days ago

对于我们人类来说,环境就在我们周围,无所不包:自然环境、社会环境等等。甚至有专门的政府机构来对我们的环境进行保护。通常评价一个人的时候,我们也会考虑,这个人所处的环境,甚至是这个人一生是在什么环境下成长和发展起来的。

环境是一个抽象而复杂的概念,可能只是表示你周围可见范围内的几棵树、几个人,也可能指的是你不知道的那些幕后故事或者去年年初发布的政策等等。

计算机,或者说程序设计也是一样。我们提到环境时,各种软件或者硬件的细节都会影响着表达。比如,微处理器的架构、内部存储的容量、操作系统的类型和和版本等等。每一个都无数种可能,这些东西组合起来又是无数种可能。这些可能性的结果就是,每个人在进行程序设计时的环境都有可能是不同的。

这种不同可能导致的一个结果就是,程序设计或者运行的结果也不一样。正如在不同的环境下成长的人也必然不同一样。但可以确定的是,即便在不同环境下成长起来的人,也能够从事完全相同的工作:这当然是因为,虽然成长或者工作环境是不一样的,但是不妨碍在某些方面有一致性的体现,而正是这种一致性让他们相互之间做的工作也是一样的。

反映到计算机上来,即便是一部分细节是可变的或者说不同的,只要有一些固定的点是经过规定或者限制,有某种一致性的话,就可以得到同样的结果。这也就是为什么本书能够给大家讲这些东西的原因:我们如果遵从这种一致性,就能相互合作、分享和进步。而本书中的这些规律,也可以完全适用于任何能够达到特定要求的环境中。

环境的层次

我们来看一下对于程序设计来说,可以怎样对环境进行划分。

平台

首先是平台(Platform)。这样一个简单的词表示了一堆信息的组合:操作系统类型和版本号以及微处理器架构:比如Linux-3.7-x86,Windows 8.1 (64bit),Mac OS X El Captain。

当然这里的某些信息是隐含的。 Linux/Windows/Mac OS X可以看成是不同的系统类型。3.7,8.1和El Captain则是系统版本(Version)。x86、64bit(Windows 8.1只针对x86-64有64位版)、El Captain(Mac OS X只发布了针对x86-64微处理器架构的版本)则可以看出来微处理器架构。

编程语言

其次是编程语言。当然一个设计良好的编程语言应该是平台无关的,也就是说,在任何平台下(至少我前面提到的三种),都可以正常地进行程序设计以及运行。当然这里的平台无关并不是说Java的“一次编译,到处运行”这种概念。而是在语言设计中就应该刨除不同平台的差异。所以,在Windows下,输出Hello world和在Mac下输出Hello World不应该有行为上的区别。当然至于操作系统如何实现,与语言无关。

当然,对于C/C++这些语言,在不同平台上编译出来的程序,一般是不能直接拿到另一个平台上用的。当然,有很多种做法来解决这个问题,第一种叫做交叉编译,也就是说,在A平台上编译针对B平台上的程序,然后再拿到B平台上去执行,这种做法在嵌入式领域很常见:往往一个嵌入式平台是不具备自己去编译程序的能力的;另一种方法就是抽离一套平台无关的运行时结构,在不同的平台上实现以后,把程序代码编译成改结构可以解析执行的程序,也就是Java常常提及的“一次编译,到处执行”的概念。

编程语言的版本

第三个重要的点是编程语言的版本。比如我在引言中很明显的标注出了本书所使用的语言是ISO/IEC 14882:2011中所定义的语言,通常也称之为C++11。其他的编程语言,比如Python,也有Python 2和Python 3的区分,比如Java,也有Java 7和Java 8。当我们提到某一个版本的时候,对于不同的语言,是有不同含义的。比如Java,其版本和语言是由Oracle公司的Java Language Specification所规定。比如Python,因为没有一个统一的文档作为spec,我们通常会指定Python的官方实现(即通常所说的CPython)的行为,作为改语言对应版本的行为。另外比如C++,在ISO有统一的标准委员会来管理和制定标准,所以任何时候有分歧的话,只要参照标准中的内容就可以了。

通常大家会混淆的一点就是语言和实现。比如Java,其实在语言规范中并没有强行规定必须编译到Java虚拟机字节码,只是说通常可以这么做然后你去参考一下Java虚拟机规范里面要怎么搞。所以,gcc中的Java实现就是可以直接编译到对应平台程序的,Android平台可以做AOT(Ahead-of-Time)编译,或者编译的目标代码是Dalvik字节码:你当然不能说他用的不是Java。

这个时候,语言所存在的意义就是规定这些实现必须做什么,不能做什么,哪些是对的,哪些是错的,哪些是未定义/未指定的行为。所以,很多人讨论语言特定(一个典型的例子比如指针)的时候,往往会陷入这个坑里:拿编译器产生的代码来描述指针的本质。这样做完全是本末倒置:语言规定了一个语言特性的行为(语义),编译器只要能够通过某些做法达到相应的要求就可以了,在这个基础之上可以做任何程度的优化来提升程序运行的时候的效率。于是,开不开优化和开多少程度的优化,编译器产生的程序可能是不一样的,但并不是说这样运行之后就得不到我们期待的结果。

本书会避免这一误区,通过语义和行为层面上的分析,而不是深入无谓的实现细节来给大家讲述程序设计思想。对于不同平台和不同的编译器实现,尽最大可能保证所有的实例代码结果是一致的。

编译器和集成开发环境

前面提到的各种环境,其实只是对程序设计的一些硬性要求。毕竟,对于一个程序来说,这些环境都是必要的而且是重要的。

而从另外一个视角来看,环境在程序设计的不同阶段

运行时环境