1、机器级程序将存储器视为一个非常大的字节数组,称为虚拟存储器(virtual memory)。存储器的每个字节都由一个唯一的数字来标识,称为地址(address),所有可能地址的集合就称为虚拟地址空间(virtual address space)。虚拟地址空间是一个展现给机器级程序的概念性映像(image),具体的实现使用的是随机访问存储器RAM,磁盘存储,特殊硬件和OS软件的结合,来为程序提供一个看上去统一的字节数组。
2、指值有两个方面:它的值和它的类型。它的值表示的是某个对象的位置,而它的类型表示那个位置上所存储对象的类型(如int,float)。
3、每台计算机都有一个字长(word size),指明整数和指针数据的标称(nominal size)大小。虚拟地址是以字来编码的。对于一个字长为n位的机器来说,虚拟地址的范围为0~2n-1,程序最多访问2n个字节。
如32位字长的计算机上,虚拟地址空间为4GB。
4、计算机和编译器使用不同的方式来编码数字。
小端(little endian):低地址存放低字节。
大端(big endian):高地址存放低字节。
网络应用程序的代码编写必须遵守已建立的关于字节顺序的规则,以确保发送方机器将它的内部表示转换成网络标准,而接收方机器将网络标准转换成它的内部表示。
示例代码
#include <iostream> #include "string" #include "cmath" #include "iomanip" using namespace std; typedef unsigned char* byte_pointer; void show_bytes(byte_pointer bpstart, int ilen) { int iTempi; for (iTempi = 0; iTempi < ilen; iTempi++) printf("%.2x", bpstart[iTempi]); printf("\n"); } void show_int(int ix) { show_bytes((byte_pointer)&ix, sizeof(int)); } void show_float(float fx) { show_bytes((byte_pointer)&fx, sizeof(float)); } void show_pointer(void *x) { show_bytes((byte_pointer)&x, sizeof(void*)); } int main() { int iTemp = 1234; show_int(iTemp); float fTemp = (float)iTemp; show_float(fTemp); int *pTemp = &iTemp; show_pointer(pTemp); return 1; }
5、C中的移位
移位运算从左至右结合。
1)机器支持两种形式的右移:逻辑的算术的。逻辑右移k位在左端补k个0,[0,...0,xn-1,xn-2,...]。算术右移是在左端补k个最高有效位的拷贝,[xn-1,...xn-1,xn-2,...]
对于无符号数据,右移必须是逻辑的,有符号数则两者都可以,一般机器实现中实现为算术右移。
2)左移,就是移位后,在右边补0。
6、要将一个无符号数转换为一个更大的数据类型,只要简单的在开头添加0;这种运算称为0扩展(zero extension)。要将一个二进制补码数字转换为一个更大的数据类型,执行符号扩展(sign extension),在表示中添加最高有效位的值。
7、在数据转换中,由大数据转换成小数据可能发生截断。弃高k位。对于一个无符号数x,截断它到k位的结果就相当于计算x mod 2k。
8、范围0,≤x,y≤2w-1内的整数x,y,可以被表示成w位的无符号数字,它们的乘积x.y的取值范围为0~(2w-1)2,这可能需要2w位来表示。
9、在大多数机器上,整数乘法指令相当慢,需要12或更多时钟周期,其他整数运算,如加法,减法,移位和位级运算,只需要1个时钟周期。因此编译器使用的一项重要的优化就是试着用移位和加法运算的组合来代替乘以常数因子的乘法。
整数除法比乘法更慢,需要30或者更多的时钟周期,除以2的幂也可以用移位运算来实现。我们可右移。对于无符号数和二进制补码数,分别使用逻辑移位和算术移位来达到目的。
10、在当前的计算机中,整数是以补码表示的。
11、IA32处理器有特别的存储器元素,称为寄存器,当计算或使用浮点数时,用来保存浮点值。浮点数的取反就是对符号位取反。
关于浮点数,本书中讲的比较零碎,可以参考国内教材中《计算机组成原理》等相关书籍。
<Computer Systems:A Programmer's Perspective>