程序结构和执行——信息的表示和处理

        本章我们研究在计算机上如何表示数字和其他形式数据的基本属性,以及计算机对这些数据执行操作的属性。

1.计算机内的信息

        大家都知道,现代计算机存储和处理的信息常以二值电信号表示,这反映到数字上就是二进制。然而正是这些微不足道的二进制数字,带来了21世纪的数字革命,我们也称这些二进制数字为位(bit)。对于人类来说,使用十进制表示法才是最正常的事情,因为人类通常都有十个手指,但是当构造存储和处理信息的机器时,二进制工作得更好。二值信号能够很容易地被表示、存储和传输,例如,在计算机刚被发明的时候,人们使用穿孔卡片上有洞或无洞来表示二进制。现在人们则常用导线上的高电压或低电压,或者顺时针或逆时针的磁场来表示二值数字。对二值信号进行存储和执行计算的电子电路非常简单和可靠,制造商能够在一个单独的硅片上集成数百万甚至数十亿个这样的电路。
        当我们孤立的看待单个位时,它显得不是非常有用。然而,当把位组合在一起,再加上某种解释(inter-pretation),即赋予不同的可能位模式以含意,我们就能够表示任何有限集合的元素。比如,使用一个二进制数字系统,我们能够用位组来编码非负数。通过使用标准的字符码,我们能够对文档中的字母和符号进行编码。在本章中,我们将讨论这两种编码,以及负数表示和实数近似值的编码。
        我们研究三种最重要的数字表示。无符号(unsigned)编码基于传统的二进制表示法,表示大于或者等于零的数字。补码(two’s-complement)编码是表示有符号整数的最常见的方式,有符号整数就是可以为正或者为负的数字。浮点数(floating point)编码是表示实数的科学记数法的以2为基数的版本。计算机用这些不同的表示方法实现算术运算,例如加法和乘法,类似于对应的整数和实数运算。计算机的表示法是用有限数量的位来对一个数字编码,因此,当结果太大以至不能表示时,某些运算就会**溢出(overflow)**。溢出会导致某些令人吃惊的后果,而溢出也常常发生,几乎每个程序员都会遇见这样的错误。

        通过研究数字的实际表示,我们能够了解可以表示的值的范围和不同算术运算的属性。为了使编写的程序能在全部数值范围内正确工作,而且具有可以跨越不同机器、操作系统和编译器组合的可移植性,了解这种属性是非常重要的。 后面我们会讲到,大量计算机的安全漏洞都是由于计算机算术运算的微妙细节引发的。在早期,当人们碰巧触发了程序漏洞,只会给人们带来-一些不便,但是现在,有众多的黑客企图利用他们能找到的任何漏洞,不经过授权就进人他人的系统。这就要求程序员有更多的责任和义务,去了解他们的程序如何工作,以及如何被迫产生不良的行为。

2.信息存储

  • 字数据大小

        每台计算机都有一个字长(wordsize),指明指针数据的标称大小(nominal size)。 也由于虚拟地址是以这样的一个字来编码的,所以字长决定的最重要的系统参数就是虚拟地址空间的最大大小。 也就是说,对于一个字长为w位的机器而言,虚拟地址的范围为0 ~ 2^w-1,程序最多访问 2^w 个字节。 而当下的个人计算机几乎都由32位变成64位了,这体现了计算机硬件技术的发展。32 位字长限制虛拟地址空间为4千兆字节,即4GB,也就是说,刚刚超过4X10字节。扩展到64位字长使得虚拟地址空间为16EB,大约是1. 84X109字节。        

        大多数64位的机器可以兼容32位的机器编译的程序,但32位机器无法兼容64位机器编译的程序,即只能向下兼容。

        为什么低位长的机器不能跑高位长机器编译的程序呢?

        

        如图:不同位长的机器,对应的数据结构的大小是不一样的,低位机器跑高位程序自然会出现溢出现象。

        不过后来为了避免由于依赖标称大小和不同编译器设置带来的奇怪行为,ISO C99中引入了一类数据类型,其数据大小是固定的,不随编译器设置和机器设置而变化。例如:int32_t 和 int64_t。

  • 寻址和字节顺序

        当我们面对跨越多字节的程序对象,如C语言中的数组,我们要明确两个东西:这个对象的地址是什么,以及在内存中如何排列这些字节。在几乎所有的机器上,多字节对象都被存储为连续的字节序列,对象的地址为所使用字节中最小的地址。例如,假设一个类型为int的变量 x 的地址为0x100,也就是说,地址表达式 &x 的值为0x100。那么,(假设数据类型int为32位表示) x 的4个字节将被存储在内存的0x100、0x101、0x102 和0x103 位置。

        排列表示一一个对象的字节有两个通用的规则——大端法(big endian)和小端法(little endian)。 小端法在内存中按照从最低有效字节到最高有效字节的顺序存储对象,大端法则从最高有效字节到最低有效字节的顺序存储。

        

2.数值信息表示

  • 原码

        正数的符号位用 “0” 表示,负数的符号位用 “1” 表示,其余数位表示数值本身。

  • 反码

        正数的反码与其原码相同;负数的反码是在原码的基础上保持符号位不变,其余
各位按位求反得到的。

  • 补码

        正数的补码与其原码相同;负数的补码是在原码的基础上保持符号位不变,其它的数位1 变为 0 , 0 变为 1,最后再加1运算。也就是说,负数的补码是它的反码加1。在计算机中,有符号整数常常用补码形式存储。

补码的优势:

        省去计算机判断符号位的麻烦。采用补码表示后,不管是加法还是减法都是加法运算。且保证了系统编码的连续性和一致性。补码可以使计算机统一加减运算,提升计算机的效率