文章目录
CSAPP第二章知识点归纳(信息的表示和处理)
基本概念
- 无符号编码基于传统的二进制表示法,表示大于零或者等于零的数字
- 补码(two’s-complement)编码是表示有符号整数的最常见方式(正负数)
- 浮点数编码是表示实数的科学计数法的以2为基数的版本(近似表示,不是精确的)
- 结果太大不能表示就会发生溢出
信息存储
- 内存的每个字节都由一个唯一的数字来标识,称为它的地址,所有可能的地址的集合就称为虚拟地址空间
- 在C语言中,十六进制数以 0x 或 0X 开头
- 进制转换的方法(虽然简单,但有可能忘记,还是摘抄下来!)
- 对于一个w位的机器而言,虚拟地址的范围是 0~ 2 w − 1 2^w-1 2w−1 ,程序最多访问 2 w 2^w 2w字节
- 下图为32位和64位计算机数据类型的典型的大小
字节顺序(大端法和小端法)
- 最低有效字节在最前面的方式称为小端法,最高有效字节在最前面的方式叫大端法
(需要注意的是上图中,是字节的顺序的不同,在单个字节的八个比特中是按顺序的)(简单记忆:按正常顺序,容易读的是大端法)(大端小端指的是单个数据对象的存储方式,而不是其他的所有字节序列,只有数据是有这两种区分的)
- 打印程序对象的字节表示的代码
#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]); //line:data:show_bytes_printf
printf("\n");
}
void show_int(int x) {
show_bytes((byte_pointer) &x, sizeof(int)); //line:data:show_bytes_amp1
}
void show_float(float x) {
show_bytes((byte_pointer) &x, sizeof(float)); //line:data:show_bytes_amp2
}
void show_pointer(void *x) {
show_bytes((byte_pointer) &x, sizeof(void *)); //line:data:show_bytes_amp3
}
void test_show_bytes(int val) {
int ival = val;
float fval = (float) ival;
int *pval = &ival;
show_int(ival);
show_float(fval);
show_pointer(pval);
}
表示字符串
- C语言中字符串被编码为一个以null(值为0)字符结尾的字符数组(一般使用的是ASCII标准)
- Unicode标准的相关知识重点:
- 使用32位表示字符,占用4个字节
- 替代编码,常见的只用1个到2个字节,不常用的要多一些的字节
- UTF-8表示将每个字符编码为1个字节序列,使得ASCII中的和UTF-8中表示一样
布尔代数
- C中的位级运算
- C中的逻辑运算(与位级运算区别,逻辑运算认为所有非零的参数都是True,而参数0表示False,返回0和1)
整数表示
下面这张表还是看看,说不定考试直接用了不知道是什么
补码编码
-
补码编码将字的最高有效位解释为负权(下面的Tmin和Tmax为能表示的最大和最小的数)
B 2 T w ( x ⃗ ) ≐ − x w − 1 2 w − 1 + ∑ i = 0 w − 2 x i 2 i T M i n w ≐ − 2 w − 1 T M a x w ≐ ∑ i = 0 w − 2 2 i = 2 w − 1 − 1 ∣ T M i n w ∣ = ∣ T M a x w ∣ + 1 B2T_w(\vec{x})\doteq-x_{w-1}2^{w-1}+\sum_{i=0}^{w-2}x_i2^i\\ TMin_w\doteq-2^{w-1}\\ TMax_w\doteq\sum_{i=0}^{w-2}2^i=2^{w-1}-1\\ |TMin_w|=|TMax_w|+1 B2Tw(x)≐−xw−12w−1+i=0∑w−2xi2iTMinw≐−2w−1TMaxw≐i=0∑w−22i=2w−1−1∣TMinw∣=∣TMaxw∣+1- 例子:
- 有符号数的另外两种标准(不重要,了解)
-
有符号数和无符号数之间的转换
强制类型转换保持位值不变,只是改变了解释这些位的方式
-
补码转换为无符号数(相互之间的转换)
T 2 U w ( x ) = { x + 2 w x < 0 x x ⩾ 0 U 2 T w ( x ) = { u u ⩽ T M a x w u − 2 w u > T M a x w T2U_w(x)=\begin{cases} x+2^w&x<0\\ x&x\geqslant0 \end{cases}\\ U2T_w(x)=\begin{cases} u&u\leqslant TMax_w\\ u-2^w&u>TMax_w \end{cases} T2Uw(x)={x+2wxx<0x⩾0U2Tw(x)={uu−2wu⩽TMaxwu>TMaxw
相互之间的转换图解:
- C语言在执行一个运算数是有符号而另一个是无符号的时候,会隐式地将有符号参数强制类型转换为无符号数(C语言默认创建有符号数,无符号数要在末尾加u)
符号扩展
B
2
T
w
+
k
(
[
x
w
−
1
,
.
.
.
,
x
w
−
1
⏟
k
次
,
x
w
−
1
,
x
w
−
2
,
.
.
.
,
x
0
]
)
=
B
2
T
w
(
[
x
w
−
1
,
x
w
−
2
,
.
.
.
,
x
0
,
]
)
B2T_{w+k}([\underbrace{x_{w-1},...,x_{w-1}}_{k次},x_{w-1},x_{w-2},...,x_0])=B2T_w([x_{w-1},x_{w-2},...,x_{0},])
B2Tw+k([k次
xw−1,...,xw−1,xw−1,xw−2,...,x0])=B2Tw([xw−1,xw−2,...,x0,])
- 就是说比如111表示的是-1,那么符号扩展了以后,11111也是表示-1,将符号位(最高位扩展)
截断数字
- 无符号数的截断就是直接将高位的除去,相当于mod 2的k次方
- 补码的截断和无符号数的一样,把它当作无符号数,也就是直接截断高位,然后在按补码的规则计算值
整数运算
无符号数加法
- 相关的图片解释
无符号数求反
- 相当于是加起来等于0(忽略进位)
补码加法
- 先把它当作无符号数进行运算,然后在转换成补码
补码的非
无符号乘法
补码乘法
-
无符号和补码的乘法结果在截断后是相同的(比如上面三位的例子),原本是不同的,原理书上P67,不用看
-
整数乘法比移位和加法的代价大很多,很多C编译器使用以移位、加法和减法的组合来消除整数乘以常数的情况
除以二的幂的补码除法,向下舍入
- 非负的数与无符号数除以二的逻辑右移效果是相同的,向下舍入到最接近的整数,但对于负数来说,除法会产生向下舍入,而不是正确的向零舍入
偏置
-
为了使负数补码除以二是向零舍入的,需要在移位的时候添加 偏置 来修正原来不合适的舍入
C变量x和k分别有补码值x和无符号数值k,且0$ \leqslant k k k < w , 则 当 执 行 算 术 移 位 时 , C 表 达 式 w,则当执行算术移位时,C表达式 w,则当执行算术移位时,C表达式 (x+(1<<k)-1)>>k 产 生 数 值 产生数值 产生数值 \lceil x/2^k\rceil $(这个是对于负数的,非负数不用)
浮点数(重点)
IEEE浮点表示
-
标准表示方法
V = ( − 1 ) s × M × 2 E V=(-1)^s\times M\times 2^E V=(−1)s×M×2E-
其中 s 表示符号,负数为1,正数为0
-
M 是尾数
-
E 是阶码
-
标准浮点格式
-
- 三种情况
- 浮点数的例子(容易忘记偏置,特别是非规格化的是1-bias,尾数前面隐含有一个1(规格化))
浮点数的舍入
- 四种舍入方式的比较
- 其中向偶数舍入在中间的值的时候是向最靠近的偶数进行舍入,其他的情况下按最接近的数字进行舍入
- 向偶数舍入举个例子,如10.110舍入到 1 2 \frac{1}{2} 21,因为要舍入的末尾为1000正好为中间,所以向偶数舍入就是舍入后末尾为0,所以结果为11.0