转至老陈的blog http://www.138soft.com/ 稍作修改。
前言
自1946年第一台电子计算机(以下简称“计算机”)诞生至今,已经过了半个多世纪。现在计算机已经融入人们的生活中,与人们的生活息息相关。不管是计算机硬件,还是计算机软件,都有了令人惊叹的发展。
计算机由两部分组成:硬件和软件。其中硬件就相当于人的躯体,而软件则相当于人的灵魂。计算机发展到今天,已经形成了一个庞大的体系,其中任何一个分支都足以耗尽一个人一生的精力。当然,今天运行在所有计算机设备上的软件,是全世界软件开发者共同的智慧结晶,不是微软一个人的功劳,更不是INTEL一个人的功劳,它是一个完整的体系,全世界共同耕耘几十年的成果,一步一个脚印,更是难以计数的金钱砸出来的。
本书主要介绍Windows操作系统下的软件程序开发,分为初、中、高级三部分。其中初级部分的课程安排如下:首先介绍计算机工作的原理,然后从控制台程序开始入门,熟悉和了解计算机编程中的一些流程,接着介绍基于SDK方式的图形界面程序编程,再接着介绍面向对象编程思想,最后进入VCL类库编程。
课程之所以这样安排,是有原因的。有很多半路出家的程序员,一开始就接触VCL,这种做法,对于入门的确很快,因为随便动动鼠标,拖几个控件,就能写出一个程序。问题在于,如果只是学会了使用VCL,那么将无法进一步提高水平,更加不用说理解其内在的本质了。其实,世界上所有的智慧都是相通的,虽然很多事情表面看属于不同范畴,但其实里面包含的道理都是一样的。比如说,生活中,相同的一个问题,甲处理起来总是比乙处理的好,这里除了可能甲的经验丰富外,他掌握关于该问题相关的信息和资源可能会比乙多很多,这就是所谓信息不对称原理。杜勒鲁奇说过:“从起源中理解事物,就是从本质理解事物。”,温斯顿.丘吉尔也说过:“你对以往知道的越多,对未来就看的愈远。”,而马基维里在《君王论》里面也说过:“人的本性千古不变,以前发生过的事情,以后也会发生”(所以读文科的人为什么一定要精通历史)。本书先从最早期的程序编写方式入手,然后一步一步走到当今的编程方式,让你知道所有事情的来龙去脉,使你“既知其然,亦知其所以然”。
当然,这种课程安排,刚开始会有一定的阵痛期。但是过了这个阶段,将迎来快感期,以后遇到问题会触类旁通,豁然开朗。当你熟悉这一切之后,再学其它语言,例如,C语言,或JAVA语言,一般地说,一个晚上即可入门,一个星期即可入手,一个月即可非常熟练。所以,这个做法也可以看作是“磨刀不误砍材工”。先练内功,等你内功深厚,其它具体的技术都是些花招而已。
第一章 计算机基础知识介绍
第一节、计算机是如何工作的
我们平时使用计算机上网、聊天、玩游戏等等,但是相信很少人想过它背后是如何工作的。本章主要介绍一下计算机工作的原理,为后面学习编写程序打下基础。这章介绍的知识只需要大概知道即可,不用死记硬背。编写程序理论上不需要对计算机的原理懂多少,但是可以想象一下,假设有甲、乙两个人,一起去学汽车修理,其中甲曾经在汽车生产车间工作过几年,对汽车的构造非常了解,而乙对汽车构造一无所知,虽然两个人同时去学习修理汽车,但是日后真的遇到问题,你觉得谁会更加容易解决?
知识点一:计算机软件工作的过程,实际上是数学运算的过程。
可能很多人都玩过“打飞机”的游戏:屏幕底部有一台飞机,玩家可以使用箭头键控制它前后左右飞行,点击空格键的时候,它会向正前方发射子弹。另外,屏幕顶端会随机出现敌人的飞机,它会不定时、不定向的发射子弹,如果玩家的飞机撞到敌机,或敌机的子弹,则玩家的飞机会爆炸。游戏的规则就是,玩家控制自己的飞机躲开敌机,同时发射子弹摧毁敌机。
让我们用数学来解释一下里面的工作原理。
在计算机世界,屏幕是有坐标系的,例如,分辨率800X600的屏幕,坐标系如下:
实际上,我们平时在电脑屏幕上面看到的一切,不管是文字还是图片,都是由“点”组成的。分辨率800X600的屏幕,实际上就是由800X600个不同或相同颜色的“点”构成,每个点都有自己的X、Y坐标。例如,在屏幕的中间显示一个红色的点,实际上就是在坐标(x=400,y=300)位置输出一个红色的象素。所以用户看到的飞机、子弹等等,都是这样绘制出来的。
假设飞机现在的坐标是(x=400,y=300),当用户按下右箭头键的时候,计算机会先把原来飞机坐标所占用区域象素清空,然后在坐标(x=401,y=301)的位置重新绘制,这样一来,就实现了飞机向右移动的操作(当然,为了显示流畅和不造成闪烁,也可能是先在内存里面绘制好,然后直接贴到新坐标,这个这里不作深入,因为不是本章的重点)。同理,飞机的其它方向的移动,其实也是坐标的加减而已。
玩家的子弹,也是这么个原理。如何知道子弹击中了物体呢?其实就是小学数学里面的坐标相交,两个坐标相交,说明两者碰到了,于是画爆炸画面。
至于游戏的音乐之类,其实是电脑控制声卡的端口,从而发出不同频率的声音,从而完成文件到声音的过程。具体往哪个端口输出哪个数字,从而决定了声卡发出多少频率的声音,这也是数学问题。
知识点二:计算机内部都是二进制的。
我们平时实际生活中,使用的都是十进制,也就是说,用0,1,2,3,4,5,6,7,8,9十个数字来表示,逢十进一。十进制的起源,可能是因为人类有十个手指,所以方便教学和演示。但计算机硬件因为是电子元件组成的,而两种稳定状态的元件很容易找到(例如电源的接通与断开、晶体管的导通和截止、继电器的接通和断开等等),所以计算机世界是使用二进制的,也就是说,使用0和1的不同组合来表示需要的数字,逢二进一。二进制的加法很简单:0加0等于0,1加0等于1,1加1等于10(注意:不是十,而是二进制的2)。
进制之间的不同转换,这里暂时不作介绍,我们使用Windows系统自带的“计算器”程序来演示一下。首先,我们打开“计算器”程序,界面如下(XP系统):
我们选择“查看”===>“科学型”,得到如下界面:
现在我们用二进制来计算十进制的11+12,首先得到十进制数11对应的二进制:将进制切换到十进制,输入数字11:
然后将进制切换到二进制,得到相应的数字:
也就是说,十进制的11转换成二进制就是1011。重复上面步骤,得到十进制数12的二进制数为1100。
1011
+ 1100
_________________
10111
再切换回“计算器”程序,可以知道,二进制的10111等于十进制的23。
经过上面的过程,我们可以知道,数字运算,不管使用什么进制,运算后结果都是一样的,只是表示的形式不一样而已。就好比中文的“你好”,英文的“Hello”,虽然字面不同,但表示的东西都是一样的。
知识点三:布尔代数与逻辑电路。
了解了上面两个知识点后,我们还不能制造出一台计算机。我们还必须先了解一下布尔代数。
布尔代数,又称为逻辑代数,是英国数学家乔治.布尔发现的。布尔代数在代数学(代数结构)、逻辑演算、集合论、拓扑空间理论、测度论、概率论、泛函分析等数学分支中均有应用;1967年后,在数理逻辑的分支之一的公理化集合论以及模型论的理论研究中,也起着一定的作用。近几十年来,布尔代数在自动化技术、电子计算机的逻辑设计等工程技术领域中有重要的应用。
布尔代数跟传统数学不同,它研究的更偏向于数学模型,把复杂的问题简化到逻辑的层面,例如集合、交集等等。我们这里只介绍简单的几个运算。
布尔代数的逻辑运算中,它只有两个值:真或假。其中二元素运算中,有如下逻辑:
“与”运算:又称为and 运算。规则如下:“真”与“真”进行与运算,结果为“真”;“真”与“假”运算,结果为假;“假”与“假”运算,结果为“假”。
也就是说,两个逻辑值运算,只有都满足“真”,结果才能为“真”,否则就为“假”。
我们用二进制来表示一下。布尔代数里面,只有“真”和“假”两个值,所以可以使用二进制来表示它:1表示“真”,0表示“假”。那么“与运算”可以这样表示:
数字1 数字2 结果
1 1 1
1 0 0
0 1 0
0 0 0
简单的说,And运算:两值同为真时,结果为真。
“或”运算:又称为or运算。规则如下:“真”与“真”进行与运算,结果为“真”;“真”与“假”运算,结果为真;“假”与“假”运算,结果为“假”。
数字1 数字2 结果
1 1 1
1 0 1
0 1 1
0 0 0
简单的说,Or运算:两值同为假时,结果为假。
“异或”运算:又称为 xor 运算。规则如下:“真”与“真”进行与运算,结果为“假”;“真”与“假”运算,结果为真;“假”与“假”运算,结果为“假”。
数字1 数字2 结果
1 1 0
1 0 1
0 1 1
0 0 0
简单的说,Xor运算:两值相同,结果为假。
除了上面三个运算外,还有一个一元运算用的也非常多:
“非”运算:又称为 not 运算。规定如下:“真”的非运算结果为“假”;“假”的非运算为真。
not 1=0;
not 0=1;
那么,布尔代数跟计算机有什么联系呢?让我们看一下二进制的加法规则:1+1=0;并进1位。
数字1 数字2 结果
1 1 0 (产生一个进位)
1 0 1
0 1 1
0 0 0
跟前面的布尔代数比较,我们可以发现,结果跟XOR运算是一样的(当然,聪明的读者可能会举一反三,说乘运算跟and运算的结果一样)。
到了这里,先让我们总结一下:计算机内部的一切运算,都是数学运算;计算机内部虽然都是二进制运算,但不同进制运算,结果都是一样的。二进制运算,可以用逻辑代数来表示。到了这里,离制作计算机只有一步之遥了:如果用电子电路来表示逻辑运算。
“与”运算的电子电路:
假如我们有以下一个电路:一个电池,两个开关,一根导线,一个灯泡。当两个开关都处于断开状态,这时候灯泡没亮:
我们只把开关1合上,如下图:
这时候,灯泡仍然是不会亮的。我们再把开关2也合上:
这时候,灯泡亮了。
我们假设开关断开用0表示,合上用1表示;灯泡熄灭用0表示,灯泡点亮用1表示,可以得到如下关系图:
开关1 开关2 灯泡
1 1 1
0 1 0
1 0 0
0 0 0
这个跟前面的“与”运算结果是一样的。
再来看“或”运算。同样,我们下面的电路拥有一个电池,两个开关,一根导线,一个灯泡,当两个开关都打开时,灯泡不亮:
如果我们只把开关1合上,那么灯泡会发亮:
同样,如果我们只把开关2合上,灯泡也会发亮:
如果我们把开关1和开关2都合上,那么灯泡仍然会发亮:
我们假设开关断开用0表示,合上用1表示;灯泡熄灭用0表示,灯泡点亮用1表示,可以得到如下关系图:
开关1 开关2 灯泡
1 1 1
0 1 1
1 0 1
0 0 0
这个跟前面的“或”运算结果是一样的。
上面就是计算机的大概制作过程,当然,我们只是简单的演示了“加法机”的大概过程,实际上,计算机内部只有加法运算,对于减法、乘法之类,它会先转换成加法然后再运行的。例如,对于减法,计算机会使用“反码”(就是上面说的Not运算)、补码等方法把它变成加法来运算。
实际上,数字逻辑电路是非常复杂的,例如,下面的电路图就能表达比较复杂的运算了:
对于运算的结果,我们仍然可以使用灯泡来表示,例如我们使用8个灯泡来表示结果,灯泡亮,表示1;灯炮没亮,表示0:
例如,下面这个图:
表示二进制的00100100,转换成十进制就是36。
第二节、计算机大概发展史
1946年2月14日,世界上第一台电脑ENIAC在美国宾夕法尼亚大学诞生。
第二次世界大战期间,美国军方要求宾州大学莫奇来(Mauchly)博士和他的学生爱克特(Eckert) 设计以真空管取代继电器的”电子化”电脑–ENIACElectronic Numerical Integrator and Calculator), 电子数字积分器与计算器), 目的是用来计算炮弹弹道。 这部机器使用了18800个真空管,长50英尺,宽30英尺, 占地1500平方英尺,重达30吨(大约是一间半的教室大,六只大象重)。它的计算速度快,每秒可从事5000次的加法运算,运作了九年之久。由於吃电很凶, 据传ENIAC每次一开机,整个费城西区的电灯都为之黯然失色。
另外,真空管的损耗率相当高,几乎每15分钟就可能烧掉一支真空管,操作人员须花15分钟以上的时间才能找出坏掉的管子,使用上极不方便。曾有人调侃道:「只要那部机器可以连续运转五天,而没有一只真空管烧掉,发明人就要拍手称庆了。
工作中的ENIAC
ENIAC使用的电子管
计算机的硬件近年来发展非常快,其中有一条著名的“摩尔定律”。“摩尔定律”是由英特尔(Intel)创始人之一戈登•摩尔(Gordon Moore)提出来的。其内容为:当价格不变时,集成电路上可容纳的晶体管数目,约每隔18个月便会增加一倍,性能也将提升一倍。换言之,每一美元所能买到的电脑性能,将每隔18个月翻两倍以上。这一定律揭示了信息技术进步的速度。
因为本教程只讲述软件程序设计,所以对于硬件的介绍就不再作深入。下面主要讲述软件的历史。
要想计算机按照使用者的要求工作(运算),那么必须给它输入指令和数据。每一种计算机,由于硬件设计和内部结构的不同,就需要用不同的电平脉冲来控制,使它工作。所以每一种计算机都有自己的机器指令集,也就是机器语言。
早期的程序设计均使用机器语言。程序员们将0、1数字编成的程序代码打在纸带或卡片上,1打孔,0不打孔,再将程序通过纸带机或卡片机输入计算机,进行运算。
例如,使用8086CPU完成运算768+12288-1280,机器码如下:
101100000000000000000011
000001000000000000110000
001011010000000000000101
假如不小心将其中一个0写成1,那么将得到错误的结果。由于机器语言非常繁琐,给整个计算机产业发展带来障碍,所以后来人们发明了汇编语言。汇编语言更加接近人类的语言,例如,对于操作“将寄存器BX的内容送到AX”(寄存器是计算机CPU中可以存储数据的器件,一个CPU中有多个寄存器,AX是其中一个寄存器的代号,BX是另外一个寄存器的代号):
机器指令:1000100111011000
汇编指令:mov ax,bx
由于使用汇编语言比机器语言更加方便,所以后来程序员都使用汇编语言来代替机器语言。但是,计算机只懂机器语言,怎么办?这时候,就需要一个能够将汇编指令转换成机器语言的程序,这样的程序称为编译器。程序员用汇编语言编写源代码,再用编译器将它翻译成机器码,由计算机最终执行。
注意:汇编语言跟机器语言一样,都是依赖机器的。例如,80X86汇编语言,编译的机器码,只能在符合80X86规范的机器上才能正确运行从而得到正确结果。
虽然汇编语言比机器语言使用起来方便了很多,但仍然需要记忆很多东西。例如,汇编指令、寄存器、段地址等等。由于汇编语言依赖于硬件体系,且助记符量大难记,于是人们又发明了更加易用的所谓高级语言。在这种语言下,其语法和结构更类似普通英文,且由于远离对硬件的直接操作,使得一般人经过学习之后都可以编程。
高级语言并不是特指的某一种具体的语言,而是包括很多编程语言,如目前流行的c,c++,pascal,python,lisp,prolog等等,这些语言的语法、命令格式都不相同。
程序设计语言从机器语言到高级语言的抽象,带来的主要好处是:
1.高级语言接近算法语言,易学、易掌握,一般工程技术人员只要几周时间的培训就可以胜任程序员的工作;
2.高级语言为程序员提供了结构化程序设计的环境和工具,使得设计出来的程序可读性好,可维护性强,可靠性高;
3.高级语言远离机器语言,与具体的计算机硬件关系不大,因而所写出来的程序可移植性好,重用率高;
4.由于把繁杂琐碎的事务交给了编译程序去做,所以自动化程度高,开发周期短,且程序员得到解脱,可以集中时间和精力去从事对于他们来说更为重要的创造性劳动,以提高程序的质量。
高级语言又分为编译语言和解释语言。编译语言的意思是,程序员编写源代码,编译器经过对源代码进行语义分析,转换成汇编语言,并最终编译出机器代码,这样一来,计算机执行起来速度就非常快。而解释语言的意思是,程序员编写源代码,伪编译器经过对源代码进行语义分析,生成一种处于源代码和机器码之间的编码,计算机执行它的时候,中间必须有一个解释器再将它转换成机器码,所以执行起来速度非常慢。就好比一封中文写的文章,而阅读者只懂英文,为了让他能理解文章的内容,有两种做法:一种是将中文文章一次性翻译成英文,然后交给阅读者;另外一种是,给阅读者配备一个翻译或字典,看一句翻译一句。前者就好比编译语言,而后者就好比解释语言了。
之所以有两种类型的语言,是有历史原因的。初期计算机配置不高,如果一个程序源代码非常庞大,那么编译起来非常浪费时间(后来Borland公司首创了内存编译技术,使速度提高了N倍),而且本身内存很昂贵,如果程序特别大(例如超过64KB),那么无法一次性全部读入,所以类似BASIC语言,就使用了解释方式:执行到哪里,就从磁盘读入一段进行解释。后来的JAVA语言,也是解释语言,但是,它针对不同的机器和操作系统,准备了不同的“翻译”,例如:Windows只懂英文,你只要安装了JAVA公司的英文解释系统,就能读懂代码;UNIX只懂法文,你就为UNIX安装JAVA公司的法文解释系统。这样一来,就实现了“一次编写,到处运行”。但弊端就是必须先安装它的运行解释库,还有就是因为基于解释,运行速度会比编译语言编译的机器码缓慢。再后来,微软公司挖了Borland公司的首席设计师安德森(Turbo Pascal、Delphi的发明创造者),担任微软.NET的总架构师后,提出了.NET体系。它综合了编译语言和解释语言的优点:它跟JAVA语言一样,程序代码第一次编译的时候变成一个叫“中间运行时”的模块,但是拿到机器运行的时候,它跟JAVA不一样:第一次运行的时候,它将其编译成机器代码,所以第二次开始运行后,速度就非常快。跟JAVA一样,要想运行.NET的程序,计算机必须先安装.NET运行库。