- 无符号编码unsigned:表示大于等于零的数字
- 二进制补码编码:表示有符号整数
- 浮点数编码:表示实数的科学计数法,浮点运算是不可结合的
2.1 信息存储
字节byte是由8位的块组成的最小可寻址存储器单位,机器级程序将存储器视为一个非常大的字节数组,称为虚拟存储器。存储器的每个字节由唯一的地址标识,地址的集合称为虚拟地址空间。虚拟地址空间是展现给机器级程序的概念性映像,通过随机访问存储器RAM、磁盘存储、特殊硬件和操作系统软件的结合实现,微程序他一共一个看上去统一的字节数组。
编译器和运行时系统将存储器空间划分为更可管理单元存放不同的程序对象(程序数据、指令和控制信息)。管理存储单元在虚拟地址空间中完成,将每个程序对象视为一个字节块,程序看作一个字节序列进行管理。
2.1.1 十六进制表示法
二进制数转换为十六进制数时,如果位总数不是4的倍数,最左边一组用0补足。
2.1.2 字
字长指明整数和指针数据的标称大小,假设字长为32位(包含4个字节),虚拟地址的范围为0~2^32-1,虚拟地址空间为4GB。
2.1.3 数据大小
char*为指针
2.1.4 寻址和字节顺序
对于跨越多字节的程序对象,这个对象的地址为所使用字节序列中最小的地址,并且被存储为连续的字节序列。假设有一个十六进制值为0x01234567存放在地址范围0x100~0x103中:
2.1.5 表示字符串
字符串被编码为一个以null字符结尾的字符数组,终止字节的十六进制表示为0x00。在使用ASCII码作为字符码的任何系统上都将得到相同的结果,与字节顺序和字长无关,因此文本数据比二进制数据具有更强的平台独立性。
2.1.6 表示代码
不同的机器类型使用不同的指令和编码方式,因此同样的函数生成的机器代码不同。从机器的角度来看,程序仅仅是字节序列。
2.1.7 布尔代数和环
整数运算和布尔代数之间有很多相似点,不同点在于整数集合形成了一个称为环的数据结构。整数集合用Z来表示,有环<Z,+,*,-,0,1>,元素0和1为加法和乘法的单位元。布尔代数<{0,1},|,&,~,0,1>有相似的属性。(这部分比较抽象没太看懂,参考其它资料有以下解释)
环的特征:
- 如果环内任意两个数相加或相乘得到的结果始终在环内,则这个环就是封闭的。
- 加法和乘法满足结合性。即a+(b+c)=(a+b)+c。a×(b×c)=(a×b)×c。
- 加法中存在中性元素0,使得任意a都有a+0≡a mod m。
- 环中任何元素a都存在一个负元素-a,使得a+(-a)≡0 mod m。即加法逆元始终存在。
- 乘法中存在中性元素1,使得任意a都有a×1≡a mod m。如果a的乘法逆元存在,则可以做除法:b/a≡b×a-1 mod m。
- 找出某个元素的逆元比较困难,一般采用欧几里得算法,但也有一种简单的方法可以用来判断a的逆元是否存在:gcd(a,m)=1则a存在逆元。
- 分配法则成立,即a×(b+c)=(a×b)+(a×c)。
2.1.8 C中的位级运算
按位的布尔运算
常用于实现掩码运算,比如掩码0xFF表示最低有效8位为1,对于x=0x89ABCDEF,位级运算x&0xFF生成一个由x最低8位有效字节组成的值,即0x000000EF。
2.1.9 C中的逻辑运算
C中的逻辑运算符||、&&和!对应或且非运算,与位级运算不同的点在于逻辑运算认为所有非零的参数都表示TRUE,并且如果对第一个参数求值就能确定表达式的结果,那么逻辑运算符就不会对第二个参数求值。
2.1.10 C中的移位运算
x<<k:x左移k位,丢弃k个最高位并在右端补上k个0
x>>k:x右移k位,丢弃k个最低位并在左端补上k个0(逻辑),或者在左端补上k个最高有效位的拷贝(数学)
2.2 整数表示
2.2.1 整型数据类型
2.2.2 无符号和二进制补码编码
假设一个整数数据类型有w位,将位向量写作x,把x看作一个写成二进制表示的数就获得了x的无符号表示:
表示负数值使用二进制补码形式,将字的最高有效位解释为符号位(负权):
当符号位设置为1时表示值为负,设置为0时表示值非负。
正数的补码是原码本身,负数的补码是符号位不变,其余位取反后+1.
2.2.3 有符号数和无符号数之间的转换
B:整数数据,U:无符号表示,T:二进制补码形式
对于非负值,B2Uw(x)和B2Tw(x)会生成同样的位模式。
无符号数转换成有符号数:U2Tw(x)=B2Tw(U2Bw(x)),x为无符号数
有符号数转换成无符号数:T2Uw(x)=B2Uw(T2Bw(x)),x为有符号数
2.2.4 C中的有符号与无符号数
C执行无符号数和有符号数之间的转换时原则是基本的为保持不变。
int tx,ty;
unsigned ux,uy;
// 显式的强制转换
tx = (int) ux; // 强制转换为int类型的ux值被赋值给tx
uy = (unsigned) ty; // 强制转换为unsigned类型的ty值被赋值给uy
// 隐式的转换
tx = ux; // ux的值被转换为int
uy = ty; // ty的值被转换为unsigned
// 输出,%d:有符号十进制,%u:无符号十进制,%x:十六进制
int x = -1;
unsigned u = 2147483648; // 2^31
printf("x = %u = %d\n", x, x);
printf("u = %u = %d\n", u, u);
x = 4294967295 = -1
u = 2147483648 = -2147483648
2.2.5 扩展一个数字的位表示
在不同字长的整数之间转换同时保持数值不变,较小的数据类型转换到较大的类型很容易,将无符号数转换成更大的数据类型采用零扩展(在开头添加0),将二进制补码数字转换为更大的数据类型采用符号扩展(在表示中添加最高有效位的值也就是符号位)。
2.2.6 截断数字
截断到k位相当于计算x mod 2^k(对于二进制补码需要经过U2T转换):
2.2.7 关于有符号数与无符号数的建议
避免无符号数的使用可以减少错误
2.3 整数运算
2.3.1 无符号加法
无符号加法等价于计算模2^w的和,可以通过丢弃w+1位得到。
2.3.2 二进制补码加法
2.3.3 二进制补码的非
2.3.4 无符号乘法
w位无符号乘法运算:
2.3.5 二进制补码乘法
w位二进制补码乘法运算:
2.3.6 乘以2的幂
右边添加k个0(左移k位)相当于乘以2^k;
2.3.7 除以2的幂
左边添加k个0(右移k位)相当于除以2^k;
2.4 浮点
2.4.1 二进制小数
小数点左移一位相当于除以2,右移一位相当于乘以2
2.4.2 IEEE浮点表示
s:采用单独的符号位s直接编码
E:k位的指数域exp=ek-1...e1e0编码
M:n位的小数域frac=fn-1...f1f0编码
在单精度浮点格式(float)中,s为1位、exp中k=8位、frac中n=23位,产生一个32位的表示;双精度浮点格式(double)中,s为1位、exp中k=11位、frac中n=52位。
2.4.3 数值示例
2.4.4 舍入
2.4.5 浮点运算
浮点数的运算结果是实际运算的精确结果舍入后得到的,另外浮点加法不具有结合性。
2.4.6 C语言中的浮点
int、float、double强制类型转换:
- int->float:数字不会溢出,但是可能被舍入
- int/float->double:能够保留精确的数值
- double->float:可能溢出成+∞或-∞,由于精确度较小还可能被舍入
- float/double->int:值会向0截断