深入理解计算机系统第二章——信息的表示和处理
2.1 信息存储
- 8位的块或者字节作为最小的可寻址的内存单位
- 机器级程序将内存视为一个非常大的字节数组 / 虚拟内存
- 内存的每个字节都由一个唯一的数字来标识,称为它的地址,所有可能地址的集合就称为虚拟地址空间
- 例如,C语言中一个指针的值(无论它指向一个整数、一个结构或是某个其他程序对象)都是某个存储块的第一个字节的虚拟地址。C编译器还把每个指针和类型信息联系起来,这样都可以根据指针值的类型,生成不同的机器级代码来访问存储在指针所指向位置处的值
- 每个程序对象可以简单地视为一个字节块,而程序本身就是一个字节序列
2.1.1 十六进制表示法
- 用十六进制(hexadecimal)数来表示位模式。用十六进制书写,一个字节的值域为0016 ~ FF16
- x = 2n,当n表示成 i + 4j (其中0≤i≤3)的形式,可以把x写成开头的十六进制数为 i ,后面跟着 j 个十六进制的0。比如,x=2048=211,我们有n=11=3+4×2,从而得到十六进制表示0x800
- x = q × 16 + r,然后我们用十六进制数字表示的 r 作为最低位数字,并且通过对 q 反复进行这个过程得到剩下的数字。例如,十进制314156的转换:
- 314156 = 19634 × 16 + 12 (C)
- 19634 = 1227 × 16 + 2 (2)
- 1227 = 76 × 16 + 11 (B)
- 76 = 4 × 16 + 12 (C)
- 4 = 0 ×16 + 4 (4)
- 由此读出十六进制表示为0x4CB2C
2.1.2 字数据大小
- 对于一个字长为 w 位的机器而言,虚拟地址的范围为 0 ~ 2w-1 ,程序最多访问 2w 个字节
2.1.3 寻址和字节顺序
-
排列表示一个对象的字节有两个通用的规则:
小端法(little endian)—— 最低有效字节在最前面;
大端法(big endian)—— 最高有效字节在最前面; -
考虑一个 w 位的整数,其位表示为 [ xw-1,xw-2,…,x1,x0 ],其中 xw-1 是最高有效位,而 x0 是最低有效位。假设 w 是8的倍数,这些位就能被分组成字节,其中最高有效字节包含位 [ xw-1,xw-2,…,xw-8 ],而最低有效字节包含位 [ x7,x6,…,x0 ],其他字节包含中间的位。某些机器选择在内存中按照前一种规则从最低有效字节到最高有效字节的顺序存储对象;另一些机器按照后一种规则从最高有效字节到最低有效字节的顺序存储
-
假设变量 x 的类型为 int,位于地址 0x100 处,它的十六进制值为 0x01234567。地址范围 0x100 ~ 0x103 的字节顺序依赖于机器的类型:
(注意,在字 0x01234567 中,高位字节的十六进制值为 0x01,低位字节的十六进制值为 0x67)大端法 小端法 0x100 0x101 0x102 0x103 0x100 0x101 0x102 0x103 … 01 23 45 67 … … 67 45 23 01 … -
反汇编(disassembler)是一种确定可执行程序文件所表示的指令序列的工具
-
下面这段C代码,使用强制类型转换来访问和打印不同程序对象的字节表示。用 typedef 将数据类型 byte_pointer 定义为一个指向类型为 “unsignedchar” 的对象的指针。这样一个字节指针引用一个字节序列,其中每个字节都被认为是一个非负整数。第一个例程 show_bytes 的输入是一个字节序列的地址,它用一个字节指针以及一个字节数来指示。该字节数指定为数据类型 size_t,表示数据结构大小的首选数据类型。show_bytes 打印出每个以十六进制表示的字节。C格式化命令 “%.2x” 表明整数必须用至少两个数字的十六进制格式输出
#include<stdio.h>
typedef unsigned char *byte_pointer;
void show_bytes(byte_pointer start, size_t len) {
size_t i;
for (i = 0; i < len; i++)
printf(" %.2x", start[i]);
printf("\n");
}
void show_int(int x) {
show_bytes((byte_pointer) &x, sizeof(int));
}
void show_float(float x) {
show_bytes((byte_pointer) &x, sizeof(float));
}
void show_pointer(void *x) {
show_bytes((byte_pointer) &x, sizeof(void *));
}
2.1.4 表示字符串
- C语言中字符串被编码以一个 null(其值为0)字符结尾的字符数组。以参数 “12345” 和 6(包括终止符)来运行例程 show_bytes,我们将得到结果 31 32 33 34 35 00
- 注意,十进制数字 x 的 ASCII 码正好是 0x3x,而终止字节的十六进制表示为 0x00
- 在使用 ASCII 码作为字符码的任何系统上都将得到相同的结果,与字节顺序和字大小规则无关。因而,文本数据比二进制数据具有更强的平台独立性
2.1.5 表示代码
- 二进制代码是不兼容的,二进制代码很少的能在不同机器和操作系统组合之间移植
- 机器没有关于原始程序的任何信息,除了可能有些用来帮助调试的辅助表以外。从机器角度来看,程序仅仅只是字节序列
2.1.6 布尔代数简介
~ | NOT | 非 |
---|---|---|
& | AND | 与 |
| | OR | 或 |
^ | EXCLUSIVE-OR | 异或 |
- 位向量 a = [ 01101001 ] 表示集合 A = { 0,3,5,6 },而 b = [ 01010101 ] 表示集合 B = { 0,2,4,6 }。使用这种编码集合的方法,布尔运算 | 和 & 分别对应于集合的并和交,而 ~ 对应于集合的补。运算 a&b=[ 01000001 ],而 A∩B={ 0,6 }
2.1.7&8&9 C语言中的运算
-
位级运算:
C的表达式 二进制表达式 二进制结果 十六进制结果 ~0x41 ~[ 0100 0001 ] [ 1011 1110 ] 0xBE ~0x00 ~[ 0000 0000 ] [ 1111 1111 ] 0xFF 0x69 & 0x55 [ 0110 1001 ] & [ 0101 0101 ] [ 0100 0001 ] 0x41 0x69 | 0x55 [ 0110 1001 ] | [ 0101 0101 ] [ 0111 1101 ] 0x7D -
逻辑运算:
表达式 结果 ! 0x41 0x00 ! 0x00 0x01 !! 0x41 0x01 0x69 && 0x55 0x01 0x69 || 0x55 0x01 -
移位运算:(算术右移是在左端补 k 个最高有效位的值)
操作 值 参数 x [ 0110 0011 ] [ 1001 0101 ] x << 4 [ 00110000 ] [ 0101 0000 ] x >> 4(逻辑右移) [ 0000 0110 ] [ 0000 1001 ] x >> 4(算术右移) [ 0000 0110 ] [ 1111 1001 ]
补充
- 关于移动 k ≥ w 位
- 未完待续