‹  返回课程

第1章《启蒙》.什么是编程语言

课文
阅读量:390
技术范畴
这节课很重要,因为你必须从根子上建立一个程序员对机器语言、二进制、汇编语言、高级语言等概念的最低要求的理解。
课前导言
这节课很重要,因为你必须从根子上建立一个程序员对机器语言、二进制、汇编语言、高级语言等概念的最低要求的理解。
第1章《启蒙》.什么是编程语言
从机器语言到高级语言,一种理解过去……

0. 引子

上一节课说到:“程序是按照一定的逻辑组合的一组指令”。“过雷区”的游戏中,双方使用的是自然语言交流指令。如果游戏双方是聋哑人,那么用嘴巴说的那套指令就玩不转了——计算机聋不聋不好说,但当我们要对计算机下达指令,人类这一套得天独厚有悠久历史的自然语言,玩不转了,怎么办呢?

解决这一问题所要做的第一件事就是:制定“机器语言”——机器有了语言,我们就可以和它亲切地交流……

“等等!”突然有个同学没举手就站起来要求发言:“机器,没有生命的东西!小猫小狗有语言倒可以接受,机器也有语言,还要我们去学习,这亵渎我作为人类的尊严!我要退学!”。

得解开这个结解,不然自尊心强的同学心生学习障碍。

 

首先,语言其实不仅仅代表有声音的内容。英语、汉语等,说出来是口头语言,写在纸上是文字语言。也都有对应的哑语,一组用手势来表达的语言符号。

对应到编程这个目的,我们是需要一组用来表达计算机指令的符号,有了符号,我们就可以组合它们,以表达我们人类思维的种种逻辑,当然,组合并不是完全自由的,需要遵循特定的语法。不直接使用人类的语言,不是人类的语言太贫乏,而是因为人类语言太丰富、特别是人类语言的符号太多、语法太复杂,导致笨笨的机器无法理解。所以我们必须同样通过人类的头脑,来制定一套相对简单语言作为机器语言。

原来机器语言也是人类制定的,并且还比较简单,这样一来自尊心的问题解决了,但新的问题又来了:“既然比较简单,为什么还需要学习呢?并且听说还挺难学的?”。

原因有很多,排在第一个的是:这里的“简单”是专门为机器定制的,适合机器阅读理解的语言,对人类来说反倒很难。比如,一开始科学家设计计算机时,都是按人类的习惯采用“十进制数”来表达数据,但要设计能够理解十进制数的的电路逻辑很难搞,后来“老冯”提出了二进制。那么我们来对比一下:十进数的789和大小一致的二进制数1100010101,如果你需要理解后面那个数,是不是需要好好学习?

再者,学习编程并不是仅仅学习编程语言自身,我们往往希望通过编程来让计算机帮助人类解决某些复杂问题。编程语言再怎么复杂,要搞懂它的语法可能一年就够了,相比学习汉语或英语确实还算简单的——但是,如何用简单的工具去解决复杂的问题,这个过程本身是复杂的,是需要学习的。

结论是计算机语言必须学习,我们就从最原始的机器语言开始。

 

1. 机器语言

机器语言要简单什么程度,才能让计算机“一看”就明白呢?机器又是怎么“看”它的语言呢?

直至今天我们所用的计算机都叫做“电子计算机”,因为它们的重要组成基础,是电子元器件。以我贫溃的电子知识,我能讲出常见的电子元器件有“电阻”、“电容”;其中的电阻,我记得中学物理课上学习过:“当电压一定时,通过导体的电流与它的电阻成反比”。就这些了。

头好大!又有同学夹起书要走了,别急,学习编程并不太需要什么复杂的电子电路的知识,不过,同学们今天带算盘了吗?

中国古人发明算盘,算盘分为上下两个区,其中上区的一颗珠表示5,而下区则一颗珠表示1。对于算珠来说,基本状态有两种:“上”和“下”。所以,当我们使用算盘时,一颗珠子拨上拨下表示5或1,身为中国人,我表示这事很简单。但发明电子计算机的先哲们(包括冯·诺依曼同志)他们找到了更简单的状态:那就是适用于电子计算机的基本符号是“通电”或“断电”。

为了国家荣誉,穿越时空的我大胆地向年轻的“小冯”提出质疑:“这样设计不是浪费电子元件的强大的表达能力吗!为什么不考虑用电阻或电压值来表达呢?比如,1伏表示数字1,2伏表示数字2,3伏表示数字3……”

冯同学这么回答:“贵国的老祖宗设计算盘时,为什么不设计一种‘不上不下’的状态呢”?

“好吧,看来虽然科学家是有祖国的,但科学无国界。各位能够从我国老祖宗发明的算盘身上得到启发,并发扬光大,造福全人类,我表示祝福和感谢。好好干!未来世界每个人都会有一台计算机,你们信吗?”

算盘珠子如果存在“不上不下”的状态,就会非常容易搞出错误。想想你家的灯泡,给它220伏它亮着,给它221伏或219伏,它也是差不多亮,你认得出来吗?拿仪表量都会有误差范围呢。所以,想要让电子元件精确表达状态,彻底“断电”和“通电”是最容易做到的。

就这样愉快地决定了,这一刻冯·诺依曼在笑,发明算盘的无名祖宗也在笑,咦,连发明太极的伏羲老祖也咧着嘴,这是为什么呢!

2. 机器语言的“字母”

科学家用“0”和“1”,来表达“通电”或“断电”。不过它们仅仅是计算机语言的“基本符号”。英语中有26个字母,单词由26个字母组成,然后再由单词组成语句。我们也可以机器语言有,并且仅有两个字母,那就是“通电”和“断电”,为了表达更简捷一点,我们今后就说成是“0”和“1”(至于0是表示通电还是断电,我们不去关心了)。

 

【轻松一刻】用机器语言写份情书吧

哈哈,没想到吧,刚才谁吓唬俺们说机器语言不好学?我看它至少比英语容易13倍。让我们现学现用一下。你有男/女朋友吗?首先你们碰个面,一起约定一些“机器指令”的表示方法,比如:

0000 :你;0001 :我;0010 :老的;0011 :地方、场所;0100 :相见;0101 :想念;0111 :很、非常、那是相当的;1000 :今天;1001 :晚上;1011 :七点钟;1111 : 亲爱的。

今后,你们可以用自定义的计算机机器语言来交流了。比如,这是一封信:

“1111 0001 0111 0101 0000 1000 1001 1011 0010 0011 0100”

3. 简单认识一下二进制

因为长10个指头,所以人类采用十进制。因为电子元件有两个稳定状态(通电、断电),所以电子计算机采用二进制。

十进制组合0到9十个数字以表达所有的数字,二进制使用“0”和“1”就可以表达所有数字。算术老师说 “逢十进一” ,所以我们知道9+1=10。二进制的世界中,0+1=1,然后再往加1,即1+1=10。由此可知,十进制中的“2”,在二进制中里必须写成“10”,并且记得将它读成“壹零”。

用这种方法,就可以用0和1来表达所有数字。事实上计算机中所有数据最终都是由0和1表达:一首MP3,一张相片,一封电子邮件,一个应用程序,全是用0和1表达的。所以如果有一大串1和0,到底它是表达什么呢?这和这个数据的上下文有关。这和我们的现实世界是一个道理,比如“250610”这串数值,可以解释成“二十五万零六百一十”,也可能是山东省某地的邮政编码,甚至可以认为它是一段简谱。

那么,用只有0和1这两个“字母”的机器语言来编程的话,当然也就是满纸的0和1了——最早的程序确实是写在纸上的,只不过比我们想象的要有趣:纸是长长的纸带,而0或1则用画圈圈表示,一圈两圈三圈,有圈的地方估计是表示1?然后交给负责打孔的助手,有圈的地方打上一个孔。当程序需要被执行时,就将纸带塞给机器。机器要实现读懂这些孔,确实不复杂,比如可以用光照检测,透光表示1,不透表示0,就这样电子线路读懂了0和1。

4. 简单认识一下汇编语言

还记得我们用二进制写的那份情书吗?

“1111 0001 0111 0101 0000 1000 1001 1011 0010 0011 0100”。

我保守认为用这样的语言写情书,于增进双方爱意方面帮助不大,倒是有利内容保密和提高人类记忆力。

纯粹的机器语言实在太难记难识,先哲们立即想到要为它们制定一些助记符。因为计算机是西方人发明的(并不是唯一原因),所以助记符就是一些简短英文字母组合,这些助记符及相应的语法规则,就称为“汇编语言”。

比如,我们要实现这样一段功能:已知b等于1;c等于2;计算b + c值,并将该值赋给a 。用二进制机器语言来表达,基本是如下内容:

10001010  01010101  11000100
00000011  01010101  11000000
10001001  01010101  11001000

换成某种汇编语言记录,长这个样子:

mov edx,[ebp-0x3c]
add edx,[ebp-0x40]
mov [ebp-0x38],edx

汇编语言比机器语言稍稍“人性化”了一点,但仍然不好记忆,二者没有本质的区别,仅是一种简单的翻译,很多时候,我们把汇编语言与机器语言等同视之。

5. 高级语言

机器语言(或汇编语言,下同)对人难读、难写、难记,对机器却易读、易处理,运行效率高,占用内存少——而当时计算机的存储器昂贵,处理器功能有限,因此使用机器语言基本是必选项。幸好那时候的程序员,基本就是我们口中的科学家叔叔阿姨们,他们坚持下来了,坚持用机器语言干了好些事,其中很重要的一件事是什么呢?。

有了机器语言,硬件性能也慢慢在长进,先哲们开始正儿八经考虑人类的感受了。是得有一些“高级”点的语言,让人类写起程序来,不那么累。

这类高级语言当然是要在字面语义及语法方面,都要比机器语言更符合人类的思维习惯。尽管只是往这个方向走出一小步,它们再不是“机器语言”了,因为机器读不懂。怎么办?方法也简单,先用机器语言写一个“翻译”程序,负责将高级语言先翻译成机器语言。整件事的过程如下图所示:

 

(图 1-3 第一个“翻译”程序怎么来的?有什么用?)

图示①的步骤,程序员辛苦写出具有“翻译”功能的代码,由于它是用汇编或机器语言写的,所以计算机很容易将它变成②中的“翻译器”程序。接着,程序员改用高级语言,开心地写新的程序的代码(图中③),但写完后机器看不懂,所以无法执行。还好,翻译器程序是机器能够读懂和执行的,翻译器读入新程序的代码,就能“翻译”出机器可以读懂的版本(图中④),现在机器就可以执行它了。

“翻译器”是先哲们用机器语言干的一件相当重要的事。它的正式名称叫“编译器”。用机器语言写程序是一件挺难的事,用机器语言写编译器难上加难,有同学担心了,有一天先哲们都驾鹤西去,那编译器不是没人能改进它了?

别担心,有了第一版编译器,新的程序员大可以用高级语言写一版新的编译器的代码,然后用旧的编译器编译出新的编译器的目标程序,这个过程可以一直继续下去……你有没有一种新的担心?

〖轻松一刻〗: 编译器的“自我进化”?

旧的编译器,编译出新的编译器,这让我想到:机器人会不会根据它当前所掌握的智能,自主制造出比当前的它智能的升级版本,然后新版本机器人学习更多知识,再继续制造一个更智能的版本。终于有一天机器人“聪明”意识到,世界应该由它们来统治?

 

课后补充

下一节:从C到C++,谈谈人类的编程思维

我们要学习的“C++”,就是一门高级语言,它又来自另一门高级语言“C”。事实上这个世界有太多太多的高级编程语言。为什么会有这么多高级语言呢?前面说了:此处的“高级”是相对机器语言而言,因为它开始照顾人类了——而一说到“人”,你应该清楚了,照顾“人”要远比照顾“机器”来得复杂,因为人是多样化的。有些人喜欢被这样照顾,有些人喜欢被那样照顾……