1.信息存储
大多数计算机使用8位的块,或者字节(byte),作为最小的可寻址的内存单位,而不是访问内存中单独的位。机器程序将内存视为一个非常大的数组,称为虚拟内存。内存的每个字节都由一个唯一的数字来标识,称为他的地址,所有可能地址的集合就称为虚拟地址空间。
1.1十六进制标识法
一个字节由8 位组成。
在C 语言中,以 0x 或0X 开头的数字常量被认为是十六进制的值。
1.2字数据大小
每台计算机都有一个字长,指明指针数据的标称大小。
有符号 | 无符号 | 32位 | 64位 |
[signed] char | unsigned char | 1 | 1 |
short | unsigned short | 2 | 2 |
int | unsigned | 4 | 4 |
long | unsigned long | 4 | 8 |
int32_t | uint32_t | 4 | 4 |
int64_t | uint64_t | 8 | 8 |
char* | 4 | 4 | |
float | 4 | 4 | |
double | 8 | 8 |
为了比main由于依赖“典型”大小和不同编译器设置带来的奇怪行为,ISO C99 引入了一类数据类型,其数据大小是固定的,不随编译器和机器设置而变化:int32_t 和int64_t。
1.3寻址和字节顺序
对于跨越多字节的程序对象,我们必须建立两个规则:这个对象的地址是什么,以及在内存中如何排列这些字节。
排列标识一个对象的字节有两个通用的规则。
- 最低有效字节在最前面的方式,称为小端法
- 最高有效字节在最前面的方式,称为大端法
大多数intel 机器都只用小端模式
1.4表示字符串
C 语言中字符串被编码为一个以null 字符为结尾的字符数组。
1.5表示代码
计算机系统的一个基本概念就是,从机器的角度来看,程序仅仅只是字节序列。
1.6布尔代数简介
二进制是计算机编码、存储和操作信息的核心,所以围绕数值0和1的研究已经演化出了丰富的数学知识体系。
1.7C语言中的位级运算
C语言的一个很有用的特性就是它支持按位布尔运算。
1.8C语言中的逻辑运算
C语言还提供了一组逻辑运算符 || 、&& 和!。
1.9C语言中的移位运算
C语言还提供了一组移位运算,向左或向右移动位模式。
2.整数表示
用位来编码整数的两种不同的方式:一种只能表示非负数,而另一种能表示负数、零和正数。
2.1整型数据类型
C语言支持多种整型数据类型—表示有限范围的整数。
32位
C数据类型 | 最小值 | 最大值 |
[signed] char | -128 | 127 |
unsigned char | 0 | 255 |
short | -32768 | 32767 |
unsigned short | 0 | 65536 |
int | -2147483648 | 2147483647 |
unsigned | 0 | 4294967295 |
long | -2147483648 | 2147483647 |
unsigned long | 0 | 4294967295 |
int32_t | -2147483648 | 2147483647 |
uint32_t | 0 | 4294967295 |
int64_t | -9223372036854775808 | 9223372036854775807 |
uint_64_t | 0 | 18446744073709551615 |
64位
C数据类型 | 最小值 | 最大值 |
[signed] char | -128 | 127 |
unsigned char | 0 | 255 |
short | -32768 | 32767 |
unsigned short | 0 | 65536 |
int | -2147483648 | 2147483647 |
unsigned | 0 | 4294967295 |
long | -29223372036854775808 | 9223372036854775807 |
unsigned long | 0 | 18446744073709551615 |
int32_t | -2147483648 | 2147483647 |
uint32_t | 0 | 4294967295 |
int64_t | -9223372036854775808 | 9223372036854775807 |
uint_64_t | 0 | 18446744073709551615 |
2.2无符号数的编码
无符号数编码具有唯一性
2.3补码编码
补码编码具有唯一性
2.4有符号和无符号数之间的转换
C语言允许在各种不同的数字数据类型之间做强制类型转换。
2.5C语言中的有符号数与无符号数
C语言支持所有整型数据类型的有符号和无符号运算。
C语言允许无符号和有符号之间的转换。
2.6扩展一个数字的位表示
一个常见的运算是在不同字长的整数之间转换,同时又保持数值不变。
要将一个无符号转换为一个更大的数据类型,我们只要简单地在表示的开头添加0。
2.7截断数字
当把一个长位类型转换位短位类型是,将长位类型截断位短位类型。
3.整数运算
表达式 x<y和表达式 x-y<0会产生不同的结果。这些属性是由于计算机预算的有限性造成的。理解计算机运算的细微之处能够帮助程序员编写更可靠的代码。
3.1无符号加法
两个非负整数相加,和的范围可能会超出数据类型长度,叫做“字长膨胀”。
3.2补码编加法
对于补码加法,和的范围可能正溢出,也可能负溢出。
3.3补码的非
对w 位的补码加法来说,TMin是自己的加法的逆。
3.4无符号乘法
无符号数相乘后截断为w位的模。
3.5补码乘法
通过将2w位的乘积截断为w位来实现的。
3.6乘以常数
整数乘法指令相当慢,编译器使用一项重要的优化,试着用移位和加法运算的组合来代替乘以常数因子的乘法。
无论是无符号运算还是补码运算,乘以2的幂都可能会导致溢出。
3.7除以2的幂
在大多数机器上,整数除法要比整数乘法更慢。除以2的幂也可以用移位运算实现,只不过我们用的是右移,而不是左移。
4.浮点数
浮点表示对形如V=v*2^y 的有理数进行编码。
4.1二进制小数
理解浮点数的第一步是考虑含有小数值的二进制数字。
4.2IEEE浮点表示
IEEE 浮点标准用 V=(-1)^sM2^E的形式来表示一个数
- 符号s决定这个数是负数(s=1)还是正数(s=0),而对于数值0的符号位解释作为特殊情况处理。
- 尾数M是一二进制小数,它的范围是1~2-e,或这是0~1-e。
- 阶码E的作用是对浮点数加权,这个权重是2的E次幂(可能是负数)。
- 一个单独的符号位s直接编码符号s。
- k位的阶码字段 exp=e(k-1)...e(1)e(0)编码阶码E。
- n 位小数字段 frac=f(n-1)...f(1)f(0)编码尾数M,但是编码出来的值也依赖于阶码字段的值是否等于0。
4.3数字示例
略
4.4舍入
因为表示方法限制了浮点数的范围和精度,所以浮点运算只能近似地表示实数运算。IEEE浮点格式定义了四种不同的舍入方式。默认的方法是找到最接近的匹配,而其他三种可用于计算上界和下界。
4.5浮点运算
IEEE标准指定了一个简单的规则,来确定注入加法和乘法这样的算术运算的结果。
IEEE标准中指定了浮点运算行为方法的一个优势在于,他可以独立于任何具体的硬件或者软件实现。
前面我们看到了整数加法形成了阿贝尔群。实数上的加法也形成了阿贝尔群,但是我们必须考虑舍入对这些属性的影响。
浮点加法不具有结合性,这是缺少的最重要的群属性。
对于科学计算程序员和编译器编写者来说,缺乏结核性和分配性是很严重的问题。
4.6C语言中的浮点数
所有的C语言版本提供了两种不同的浮点数据类型:float和double。
5.小结
计算机将信息编码为位(比特),通常组织成字节序列。
C语言的设计可以包容多种不同字长和数字编码的实现。
大多数机器对整数使用补码编码,而对浮点数使用IEEE标准754编码。
在相同长度的无符号和有符号整数之间进行强制类型转换时,大多数C语言实现遵循的原则是底层的位模式不变。
由于编码的长度有限,与传统整数和实数运算相比,计算机运算具有非常不同的属性。
和大多数其他程序语言一样,C语言实现的有限整数运算的真实的运算相比,有一些特殊的属性。
浮点表示通过数字编码位x*2^y的形式近似地表示实数。
必须非常小心地使用浮点运算,因为浮点运算只有有限的范围和精度,而且并不遵守普通的算术属性,比如结合性。