第2章 信息的表示和处理

三种最重要的数字表示:无符号、补码、浮点数。
溢出:结果太大,无法表示。

2.1 信息存储

虚拟内存:机器级程序将内存视为一个非常大的字节数组;
地址:内存中的每个字节都由一个唯一的数字来标识,该数字为地址;
虚拟地址空间:所有可能地址的集合;
程序对象:程序数据、指令和控制信息。

2.1.1 十六进制表示法

十六进制:0-9,A-F;
C语言中,十六进制值前面加上0x;

2.1.2 字数据大小

字长:指明指针数据的标称大小;
相关C声明与字节数如下表:

有符号 无符号 32位 64位
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 8
float 4 4
double 8 8

2.1.3 寻址和字节顺序

排列表示一个对象的字节有两个通用的规则:
大端法,最高有效字节在最前面;小端法,最低有效字节在最前面。
使用不同方法产生的数据互通的时候,有相关标准;阅读表示整数数据的字节序列时字节顺序也很重要;编写规避正常类型系统的程序时字节顺序也很重要。
反汇编器:确定可执行程序文件所表示的指令序列的工具。
打印程序对象的字节表示,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#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++)
print(" %.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 *));
}

typedef来命名数据类型:
typedef int *int_pointer;
int_pointer ip;
也可以声明为:int *ip;

2.1.4 表示字符串

C语言中字符串被编码为一个以null字符结尾的字符数组,每个字符都由某个标准编码来表示,最常见的是ASCII字符码。

2.1.5 表示代码

对于同一个C函数,指令编码是不同的,在不同的操作系统上有不同的编码规则,因此二进制代码是不兼容的。

2.1.6 布尔代数简介

布尔代数:围绕数值0和1的数学知识体系,基本运算包括NOT(~)、AND(&)、OR(|)和EXCLUSIVE-OR(^)。
位向量:用以编码任何子集,集合中含有数字几,那么位向量从右往左第几位就为1。

2.1.7 C语言中的位级运算

利用布尔代数的性值进行运算即可。
位级运算的常见用法:掩码运算。

2.1.8 C语言中的逻辑运算

这部分对应为||、&&、!,对应于命题逻辑的OR、AND和NOT运算。
逻辑运算和位级运算的区别:
逻辑运算返回1或者0,位运算只有参数被限制为0或者1时,才和与其对应的逻辑运算有相同的行为;
对于第一个参数求值就能确定表达式的结果,那么逻辑运算符就不会对第二个参数求值(a&&5/a不会造成被零除,p&&*p++不会导致间接引用空指针)

2.1.9 C语言中的移位运算

移位计算:x<<k,向左移动k位,丢弃最高的k位,并在右端补k个0;对于右移,分为逻辑与算术右移,逻辑右移在左端补k个0,算术右移是在左端补k个最高有效位的值。
对于C语言,几乎所有的编译器/及其组合都会选择算术右移;对于java,>>是算术右移,>>>是逻辑右移。
如果移动k位,k非常大,大过操作数w,那么实际位移量为k mod w。
移位的优先级较低,加减法的操作符优先级高于移位。