1.1 信息在计算机中的表示和存储
1.1.1 用 0 和 1 表示信息
在计算机内部,所有的信息都是用 0 和 1 表示的。计算机的电路可以看作由一个个开关组成,开关只有开和关两种状态,正好对应于 0 和 1,因此,在计算机里,用 0,1 表示和存储各种信息最为方便。
比特(bit)是计算机用来存储信息的最小单位。一个比特可以由计算机电路里的一个开关来表示或存储,它只有两种取值: 0或1。一个比特,也就是二进制数的1位。8个比特组成一个字节(Byte)。1024 (210) 个字节称作 1KB, 1024KB 称作 1MB (1兆), 1024MB 称作1GB,1024GB 称作 1TB。
实际上,如果不嫌麻烦,人们可以只用 1 和 0 表示和传播各种信息。假设大家事先约定好,用 8 个连续的 0 或 1(即 1 个字节)来表示一个字母、数字或标点符号,比如用“00100000”表示空格,用“01100001”表示字母“a”,用“01100010”表示字母“b” ,用“01100011”表示字母“c”……8 个 0 或者 1 组成的串,一共有 2²即 256 种不同的组合,这就足以表示 10 个阿拉伯数字以及英语中用到的所有字母和标点符号。因此,在遵循相同的约定的情况下,一个人可以只用 0, 1 来写文章,他的读者把每 8 个 0, 1 翻译成一个字母、数字或标点符号,最终就能将这篇文章翻译成英文。
当然,在 0,1 写的文章和普通文章之间来回转换是非常麻烦的。幸好计算机不怕麻烦,所以,在计算机中,文章就是按上述的类似规则,用 0, 1 来表示并存储的。用 0, 1 串表示英文字母、汉字等字符可以有不同的规则或方案,这些规则或方案都叫作“编码”。常见的编码有ASCII 编码、Unicode编码等。ASCII 编码就是上面提到的用 1 个字节来表示数字、英文字母、标点符号的一种方案。
即便是一幅图,也可以只用0和1来表示。很多个不同颜色的点集合在一起,就能形成一幅图画。只要这些点挨得非常密,人眼就不会感觉出图画是由一个个点组成的。我们常说一台数码相机是1000万像素的,指的就是它拍出的照片是由大约1000万个不同颜色的点(像素)组成的,这些点可以组成比如3900行2600列的一个点阵。
那么如何只用0和1 来表示一幅这样的图呢?假定只有 256 种颜色可以用来画图(当然实际上可以多得多),那么图上的每一个像素就只能是这256种颜色中的一种。我们可以用1个字节给这256种颜色编号,比如用“00000000"表示第一种颜色,用“00000001”表示第二种颜色……图片上每一行有2600个点,每个点的颜色0用一个字节表示,那么一行所有的点就可以用2600个字节表示,从左数第一点对应第一个字节,第二点对应第二个字节…这样整个图片就可以用0, 1串表示出来。在计算机以及数码相机中,图像就是按上述的类似规则用0,1来表示并存储的。只要不嫌麻烦,人们也可以根据上述办法,用0, 1写出一幅图来,比如一张别人看不懂的秘密地图,收到这个0, 1 图的人根据事先约定好的对应规则,可以用颜料在画布上把所有点描绘出来,最终得到一幅普通的地图。
B125C2
计算机执行的程序,即机器指令的集合,也是由 0,1 构成的。
总而言之,计算机中的信息都是用 0,1 表示和存储的,内存、硬盘、光盘、U 盘上存放的各种可执行程序、文档、照片、视频、音乐,本质上都是一样的,都是0,1 串,都是由一个个的比特组成的。不过,它们有不同的格式,格式就是前面说的大家约定的某种信息对应到 0,1 的规则。根据不同的格式,计算机就能将图片、声音、视频等用 0,1 串来进行存储,以及从 0,1 串还原出原来的东西展现给人看。
1.1.2 二进制和十六进制
我们日常使用的是十进制数。准确地说,“十进制数”是“数的十进制表示形式”的简称。数就是数,只有大小之分,没有进制之分。只有数的表示形式,才有进制之分。正如相对论就是相对论,没有中文相对论和英文相对论之分,只有相对论著作,即相对论的表达形式,才有中文版和英文版之分。
十进制数有 10 个数字,0~9。之所以会使用十进制数,就是因为人有 10 根手指头。如果人类共有12根手指头,那么现在大家使用的就会是十二进制,而不是十进制。
计算机使用二进制数,因为它只有2根指头——其电路开关只有开和关两种状态。
原始人数数,十个指头数不过来了,就在别处记下“我已经用十个指头数过一遍”这件事(比如让第二个人伸出 1 根手指头),然后第二遍又从 1 开始数——这就是十进制数的逢十进一。
K进制数(准确说就是数的 K 进制表示形式),就是“逢 K进一”。
假设有一个n+1 位的K 进制数,它的形式如下:
那么这个数到底有多大呢?答案就是:
比如5位十进制数19085,实际上就等于
二进制数逢二进一,只能包含 0 和 1 两个数字。因此,一个比特正好对应于二进制数的1位。
如何将一个二进制数转换成我们熟悉的十进制数呢?还是用上面提到的原理。
下表列举了一些二进制数到十进制数转换的例子。
二进制数 | 转换计算过程 | 对应的十六进制数 |
---|---|---|
0 | 0×2⁰ | 0 |
1 | 1×2⁰ | 1 |
101 | 1×2⁰+0×2¹+1×2² | 5 |
10110 | 0×2⁰+1×2¹+1×2²+0×2³+1×2⁴ | 22 |
十六进制数应该包含 16个数字,可是阿拉伯数字只有 10个,于是引入“A” “B” “C”"D” "E” "F" 6个字母(小写亦可),作为十六进制的数字来使用。
"A”代表十进制的10, "B”代表十进制的11...... "F”代表十进制的15。因此,十六进制数就是由阿拉伯数字加5个字母组成的。
下表列举了一些十六进制数到十进制数转换的例子。
由于信息在计算机内都是以二进制数的形式表示的,所以在计算机学科的学习和实践中我们经常要用到二进制数,这样才能直观地看出某项数据的各个比特都是什么。
但是,二进制数位数太多,写起来和看起来搞不好要"患密集恐惧症",解决这个问题的办法就是用十六进制数。
4位二进制数的取值范围是从0000到1111,即十进制的0到15,正好对应十六进制的数字0到F。因此,十六进制数的1位,就正好对应二进制数的4位。
十六进制数和二进制数的相互转换非常直观、容易,不需要做算术,十六进制数写起来又短,所以十六进制数用起来比二进制数更为方便。
二进制数转换成十六进制数的方法,就是从右边开始,依次将每 4 位转换成 1 个十六进制位。十六进制数转换成二进制数,方法也是从右边开始,每1位转换成4个二进制位,转换结果不足4位的,要在左边补0凑齐4位(最左边一位转换时不用补 0)。
下表列举了一些十六制数和二进制数对照的例子(为了看着方便,二进制数每4 位之间用空格隔开)。
如何将一个十进制数转换成二进制数或十六进制数呢?
有通用的办法,叫作“短除法”。给定一个整数 N和进制 K,那么 N一定可以表示成以下形式:
上面式子中,
$$
Ai < K(i=0...n),
$$
且An不为0
1.2 计算机程序设计语言
1.2.1 机器语言
计算机能够执行的指令叫作机器指令。机器指令完全由0和1构成。
一台计算机有哪些机器指令,每条机器指令是什么格式以及用于实现什么功能,是由CPU(中央处理器)的设计者事先定好的,这就叫指令系统。
由机器指令组成的程序叫作可执行程序。
例如,完成一次加法运算的几条机器指令可能如下:
1000 0001 0000011000000000
1000 0010 1000000000000000
1100 0001 0010
1001 0001 0000110000000000
上面的每条机器指令都是二进制数。高四位(左边为高,右边为低)代表机器指令所要进行的操作,比如加法、乘法、将数据从内存复制到寄存器或将数据从寄存器复制到内存等。其余的部分表示要进行操作的对象。
CPU进行各种运算,都需要先将数据从内存复制到CPU中的寄存器,然后通过寄存器进行运算。
-
上面第一条指令中,高四位“1000”表示要进行将数据从内存复制到寄存器的操作;
-
“0001”表示要将数据复制到 1 号寄存器(寄存器有多个)
-
"0000011000000000"表示数据来源于内存地址 0000011000000000。不妨假定寄存器的宽度是 16位,那么有 16 位的数据被复制。
-
-
同理,第二条指令表示要把内存地址1000000000000000处的数据复制到0010号即2号寄存器。
-
第三条指令中,高四位的“1100”表示要进行加法操作,相加的两个数分别位于 1 号寄存器和 2 号寄存器,操作结果放到 1 号寄存器里。
-
第四条指令中,高四位的“1001”表示要进行将寄存器的内容复制到内存的操作。后面的部分表示要将1号寄存器的内容复制到内存地址 0000110000000000处。
上面4条指令完成了将内存地址0000011000000000和1000000000000000处的两个16位二进制数相加,并且将结果放到内存地址0000110000000000处。
用上面的办法编写程序,编写的是计算机能够理解的 0、1 中,即机器指令,因此这也被称作用机器语言编程。
在只有机器语言的时代,程序员不得不记住每一条指令的格式。写一段两个数相加的程序,就要像前面那样编写,同时还要确定相加的两个数应该放在内存的哪个位置,相加的结果又放在哪里,实在是非常麻烦。
而且,早期的计算机没有键盘,所谓编写程序,就是在纸质卡片上打孔,打孔的地方就是 0,没打孔的地方就是 1,一排孔就是一条指令,然后用专门的读卡器将卡片上的程序读入计算机的内存再运行。
1.2.2 汇编语言
机器语言用起来非常麻烦,因为要记住每个操作所对应的指令是一件很困难的事。于是出现了汇编语言。
汇编语言和机器语言的主要区别,就是将机器指令中难记的操作代码用直观的英文“助记符”来代替,比如用“ADD”代替表示要进行加法操作的“1100”,用“MOV”代替表示要进行复制数据操作的“1000”和“1001”,甚至用“AX”代替 1 号寄存器,用“BX”代替 2 号寄存器。此外,还有加标点符号、用十六进制数替代二进制数等。
1.2.1 节中的机器语言程序,可以用汇编语言编写如下:
MOV AX,0600
MOV BX,8000
ADD AX,BX
MOV OCOO,AX
显然,这比机器语言程序易写、易懂。
在汇编语言时代,程序员已经可以通过键盘编写程序,再由专门的汇编器将汇编程序编译成由机器指令组成的可执行程序。
一般来说,一条汇编指令对应一条机器指令。
1.2.3 高级语言
汇编语言虽然比机器语言方便得多,但用起来依然麻烦,程序员必须对计算机的指令语系统乃至硬件很了解,比如知道有几个寄存器,还要记住每条汇编指令的格式。
而且,用汇编语言编写的程序,是和具体的计算机系统紧密相关的,其很难在不同的计算机系统上运行。
比如,用汇编语言编写的运行在Intel 80×86上的程序,就几乎不可能在苹果公司的iPhone上运行。
人们既希望能用比较接近自然语言的语言来编写程序,又不想考虑把数据放到哪个内存地址、什么时候把数据复制到寄存器里这些和硬件相关的细节,甚至根本不想知道计算复机有几个寄存器。
而且,人们还希望编写的程序能在不同的计算机系统上运行。于是,高级语言应运而生。
高级语言有点接近自然语言,用高级语言编写前面提到的加法运算程序,只需类似下面的这条语句:
c = a + b
如果想从键盘输人a、b 的值,a、b相加后输出结果,那么用下面的几条语句就可以完成:
a = int(input()) #变量a接收用户的输入,
b = int(input()) #变量b接收用户的输入,
c = a + b #c用于接收a+b的和
print(c)
a = 10 #a的数据类型取决于用户的输入
b = 20.0 #b的数据类型取决于用户的输入
c = a + b
print(c)
input 表示输入数据,print 表示输出数据。输入的a和b需要强转为int类型。
输入数据和在屏幕上输出结果是一个很复杂的过程,如果用汇编语言编写程序,可能需要几十条甚至上百条语句,但是用高级语言来完成,一条语句就能做到,而且直观、易懂、易记。
不同的高级语言有不同的语法,并不是每种高级语言输入、输出以及做加法运算的语句都像上面的短程序那样。
之所以高级语言用起来如此方便,是因为有编译器或解释器的支持。
编译器和解释器都是将高级语言程序编译成机器语言程序的软件,但是工作方式有所不同。
需要编译器支持的高级语言,称为编译型语言;需要解释器支持的高级语言,称为解释型语言。
编译器可以将高级语言程序转换成由机器指令组成的计算机可执行文件。
这样的转换只需要做一次。
可执行文件可以被分发到不同计算机上,不需要编译器就能独立运行。
编译的过程可以类比为笔译,编译结果可以脱离翻译员被使用。
常见的编译型语言有C语言、C++语言。
解释器会将高级语言程序载入内存,在内存里逐条将高级语言语句编译成机器指令,然后执行。
解释器对高级语言程序的处理,是边编译边执行,可以类比为同声传译。因此,用解释型语言编写的程序不可以独立运行,运行时必须有解释器的支持。由于程序是边编译边执行的,所以一般来说,用解释型语言编写的程序明显比用编译型语言编写的程序运行得慢。而且,要分发用解释型语言编写的程序,需要将解释器和程序一起打包后再分发,或者要求分发的目标计算机上必须安装有解释器。
解释型语言种类繁多,比如Java、Python、PHP、JavaScript 等。
用高级语言编写的程序与硬件以及操作系统的关系不是非常密切,因此在一个系统中编写的高级语言程序经过一定的改动,并且经过针对其他系统的编译器编译后,是可以在其他系统上运行的,这个过程叫作程序的“移植”。
我们经常看到同一个软件有针对不同系统的版本,比如《植物大战僵尸》游戏,不仅有 Windows 版,还有 iPad 版、Android 版,这就是程序经过移植的结果。移植的工作量是很大的,但比针对每个系统都重新写程序省事得多。用Python编写的程序在Windows, macOS和Linux上可以互相移植,几乎不用做什么修改,只要有针对不同系统的 Python 解释器支持即可。
1.2.4 Python 简史
Python 的发明人是生于 1956 年的荷兰程序员吉多·范罗苏姆(Guido van Rossum),1982年,吉多在荷兰阿姆斯特丹大学获得数学和计算机科学硕士学位,后米在荷兰和美国的一些研究机构工作过,也曾加入Google公司和 Dropbox公司,2020年又加入微软公司。2006年吉多被美国计算机协会(ACM)认定为著名工程师。
吉多于 1989 年底开始创造 Python。那时他正好在读一本 Monty Python 喜剧团的剧本,觉得“Python”这个名字又酷又好记,Python 语言因此得名。
第一个公开发行的 Python 版本是1994年发布的, 2000年Python 2.0发布, 2008年Python 3.0发布。
Python 3.x的语法与 Python 2.x 及以前版本都不兼容(非向后兼容),即 Python 2.x 的程序在 Python 3.x 环境中无法运行。本书写作时Python的最新版本是3.12。
版本信息
某些python2代码可能依赖没有迁移到python3的第三方库,导致代码升级困难,基于迁移成本(同时更新和维护python2和python3)
区别:
Python在早期发展得并不顺利,主要原因就是慢。Python是解释型语言,用它编写的程序的运行速度比较慢,在计算机性能不够高的时代,这是一个严重的问题。随着计算机硬件的运行速度越来越快,Python的短板变得越来越无关紧要(易用性>性能优势),从2014年开始Python迎来了井喷式发展,目前其市场份额已赶超C语言和Java,成为流行的程序设计语言。
Python流行的原因
-
易学、易上手。Python语法简单、简洁且自然,不容易理解的概念和细节相对较少,非常适合非计算机专业人士学习。用Python 编写的程序,明显比用 C++、Java 等语言编写的程序更短。
-
有数量远超其他语言的、功能繁多的第三方库。这些库大多数可以免费使用。如果把编写程序比作造汽车,用C++、Java等语言编写程序,相当于要从头造轮子、发动机、变速箱等部件;而用 Python编写程序,由于可用的库很多,相当于各种部件都是现成的,只要把它们拼装起来就能造出一辆汽车。
-
用Python进行软件开发,工作效率往往是用其他语言的数倍甚至数十倍。因此, Python界有句名言:“人生苦短,我用Python。”对于非计算机专业人士来说,用Python编程,绝对是不二之选。