注:以下内容均来自开源学习组织DataWhale
1 虚拟地址空间
将内存视为一个数组,数组中的元素是由一个个字节组成,每个字节由一个唯一数字表示,这个数字称为地址,所有地址的集合称为虚拟地址空间。
2 Byte(字节)与bit(位)
进制转换:
对于 2n2^n2n(二进制表示为1后面n个0)这样的二进制数字,快速转为十六进制的方法:先根据n=i+4jn=i+4jn=i+4j计算i, j,最终十六进制表示为0x2i2^i2i后面加j个0。
例:2112^{11}211 ;11=3+2×411 =3+2\times411=3+2×4,i = 3, j = 2 对应十六进制为0x800
十进制与十六进制转换:辗转相除
3 字数据大小
字长为w位的机器,虚拟地址范围是000到2w−12^w-12w−1
例如32位机器,虚拟地址有2322^{32}232个,每个地址是1byte,即总共2322^{32}232bytes = 4GB
字长 | 地址空间 |
---|---|
w bit | 000 ~ 2w−12^w-12w−1 |
32 bit | 000 ~ 232−12^{32}-1232−1, 4GB |
64 bit | 000 ~ 264−12^{64}-1264−1, 16EB |
4 C语言中数据类型所占字节数大小
数据类型(有符号) | 数据类型(无符号) | 字节数(32位机器) | 字节数(64位机器) |
---|---|---|---|
[signed]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 |
5 寻址和字节顺序
假设一个int类型变量x(0x01234567),占4个字节,地址位于0x100处,因此变量x存储地址为0x100,0x101,0x102,0x103的内存处。
- 大端法(最高有效字节存在最前面,即低地址处)
0x100 | 0x101 | 0x102 | 0x103 | ||
---|---|---|---|---|---|
··· | 01 | 23 | 45 | 67 | ··· |
- 小端法(最低有效字节存储在最前面)
0x100 | 0x101 | 0x102 | 0x103 | ||
---|---|---|---|---|---|
··· | 67 | 45 | 23 | 01 | ··· |
注:大多数Intel兼容机采用小段模式,IBM和 Sun公司机器大多采用大端法,ARM架构的处理器支持双端法,但在Android和iOS系统之恶能运行小端模式。
6 十六进制与二进制对应关系
十六进制 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
二进制 | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 |
十六进制 | 8 | 9 | A | B | C | D | E | F |
二进制 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 |
例如:字符串"abced",其在内存中存储信息为61 62 63 64 65 00,(C语言中字符串最后一位为null,即00)这个存储信息就是十六进制的,即十六进制的ASCII编码。单独拿出"a"对应的61看,十六进制数字61转为二进制可通过上表查到,6对应0110,1对应0001,即61对应二进制为01100001。
7 布尔运算
-
非(NOT,∼\sim∼):∼0=1\sim0=1∼0=1 ∼1=0\sim1=0∼1=0
-
与(AND,&):0&0=00 \& 0=00&0=0 0&1=00 \& 1=00&1=0 1&0=01 \& 0=01&0=0 1&1=01 \& 1=01&1=0
-
或(OR,|):0∣0=00|0=00∣0=0 0∣1=10|1=10∣1=1 1∣0=11|0=11∣0=1 1∣1=11|1=11∣1=1
-
异或(EXCLUSIVE-OR,∧\wedge∧):0∧0=00\wedge0=00∧0=0 0∧10\wedge10∧1=1 1∧0=11\wedge0=11∧0=1 1∧1=01\wedge1=01∧1=0
8 C语言中的位级运算
C语言一个特性就是支持按位进行布尔运算
例如: ∼\sim∼ 0x41,二进制为 ∼\sim∼[0100 0001],运算结果为[1011 1110] ,运算结果十六进制表示为0xBE
位运算的一个常见用法就是实现掩码运算,例如:对于操作数0x89ABCDEF,如果想得到该操作数最低有效字节的值,可以通过&0xFF,就可以得到最低有效字节0x0000 00EF。
- 左移运算
左移几位就丢弃最高的几位,并在右端补相应个数的0 - 右移运算
(1) 逻辑右移:右移几位就丢弃最低的几位,并在左端补几个0
(2) 算术右移:右移几位就丢弃最低的几位,左端补1
unsigned右移是逻辑右移,signed右移一定是算术右移
9 整数的表示
- 带有正、负号的二进制数称为真值。在计算机中约定:在有符号数的前面增加一位符号位,0表示正号,1表示负号。这种数被称为机器数。机器数的编码方式有原码、反码、补码3种。
- 原码:正数的符号位用0表示,负数的符号位用1表示,其余各位数表示数值本身
例如:+1010110表示为01010110;-0110101表示为10110101 - 反码:正数的反码与其原码相同;负数的反码为保持其原码符号位不变,其余各位按位取反得到
例如:+1010110表示为01010110;-0110101表示为11001010 - 补码:正数的补码与原码相同;负数的补码为保持原码符号位不变,其他位取反,最后再加1运算,即用其反码加1。
例如:+1010110表示为01010110;-0110101表示为11001010+1即11001011
9.1 补码的意义
两种方式理解:
- 例如十进制数-5,二进制原码为1101,要得到-5的补码就需要找到与+5和为10000的二进制数(为什么这么找呢,我理解是-5补码地址位的1有了数值含义,即表示−24-2^4−24,因此需要后面4位加−24-2^4−24等于-5,反过来说就是后面4位与+5的和等于242^424(即10000)),+5原码为0101,0101与1010和为1111,1111再加1就等于了10000,所以找到的那个二进制数为1010+1,即得到-5补码为1011。
- -5的补码为1011,由于最高位有了数值意义,其表示-8,另外两位上的1分别表示+2,+1,这样就可以根据补码直接计算出补码对应的十进制数。
9.2 byte的范围
1byte=8bits,即8位。对一个8位数据来说,其补码最小数字为[1000 0000]即−28−1-2^{8-1}−28−1= -128,最大数字为[0111 1111]即28−1−12^{8-1}-128−1−1=127,所以1byte的范围为−128∼127-128\sim127−128∼127
9.3 有符号数和无符号数的转换
short int a =-12345;
unsigned short b = (unsigned short)a;
printf(”a= %d , b = %u” , a, b);
在C语言中将一个有符号数-12345(十进制)强转为无符号数,转化结果为53191(十进制)。
但两个数的二进制表示是一样的,都是1100 1111 1100 0111。
-
有符号(T)转无符号(U)满足下面公式:
T2Uw(x)={x+2wx<0xx≥0 T2U_w(x)=\begin{cases} x+2^w & x<0 \\ x & x\geq0 \\ \end{cases} T2Uw(x)={x+2wxx<0x≥0xxx:有符号数(x≥0x\geq0x≥0即最高位数字是0,x<0x<0x<0即最高位数字是1)
T2Uw(x)T2U_w(x)T2Uw(x):转换后的无符号数
www:二进制数的位数
-
无符号转有符号满足下面公式:
U2Tw(u)={uu≤TMaxwu−2wu>TMaxw U2T_w(u)=\begin{cases} u & u\leq TMax_w \\ u-2^w & u>TMax_w \\ \end{cases} U2Tw(u)={uu−2wu≤TMaxwu>TMaxw
uuu:无符号数(u≤TMaxwu\leq TMax_wu≤TMaxw即最高位数字0,u>TMaxwu>TMax_wu>TMaxw即最高位数字是1)
U2Tw(u)U2T_w(u)U2Tw(u):转换后的有符号数
www:二进制数的位数
9.3.1 转换案例
在C语言中,有符号数和无符号数进行运算时,C语言会隐式的将有符号数强制转换成无符号数来执行运算。
1 int i = -1;
2 unsigned int b = 0;
3 if(a < b)
4 printf(”−1 < 0”)
5 else
6 printf(”−1 > 0”)
执行结果为-1>0。
这是因为-1(二进制表示为32个1)被转换为无符号数,其十进制的值就变成了232−12^{32}-1232−1。
9.4 扩展与截断
9.4.1 扩展
- 无符号数转成更大的数据类型:只需在扩展的数位进行补0即可,称为零扩展
- 有符号数转更大的数据类型:
- 非负有符号数:扩展的数位补0即可
- 负数有符号数:扩展的数位需要补1
转换定理:当有符号数从一个较小的数据类型转换成较大类型时,进行符号位扩展,可以保持 数值不变。
9.4.2 截断
- 无符号数: 将一个 w 位的无符号数,截断成 k 位时,丢弃最高的 w-k 位,截断操作可以对应 于取模运算,即除以 2 的 k 次方之后得到的余数。
- 有符号数:
- 我们用无符号数的函数映射来解释底层的二进制位,这样一来我们就可以使用与 无符号数相同的截断方式,得到最低 K 位;
- 我们将第一步得到的无符号数转换成有符号数。
10 整数的运算
10.1 无符号数加法溢出
c=a+b−2wc=a+b-2^wc=a+b−2w ,其中www为二进制数的位数;
例如:0(0000 0000) = 255(1111 1111) + 1(0000 0001) - 282^828
C语言中判断是否溢出代码如下:
1 int uadd_ok(unsigned x, unsigned y){
2 unsigned sum = x + y;
3 return sum >= x; // 溢 出 返 回 0, 没 溢 出 返 回 1
4 }
10.2 补码加法溢出
- 当 x 加 y 的和大于等于 2w−12^{w−1}2w−1 时,发生正溢出,此时,得到的结果会减去 2w2^w2w
- 当 x 加 y 的和小于 −2w−1−2^{w−1}−2w−1 时,发生负溢出,此时,得到的结果会加上 2w2^w2w
10.3 加法逆元(相反数)
对于减法运算可以转化为加法逆元,即y−x→y+x′y-x\rightarrow y+x\primey−x→y+x′ (x′为x的加法逆元,也称相反数x\prime为x的加法逆元,也称相反数x′为x的加法逆元,也称相反数)
w位的无符号数逆元x′={xx=02w−xx≥0 w位的无符号数逆元x\prime=\begin{cases} x & x=0 \\ 2^w-x & x\geq0 \\ \end{cases} w位的无符号数逆元x′={x2w−xx=0x≥0
w位的有符号数逆元x′={−xx>TMinwTMinwx=TMinw w位的有符号数逆元x\prime=\begin{cases} -x & x>TMin_w \\ TMin_w & x=TMin_w \\ \end{cases} w位的有符号数逆元x′={−xTMinwx>TMinwx=TMinw
TminwTmin_wTminw:w位有符号数的最小值
TMinw+TMinw=−2w−1+(−2w−1)=−2w
TMin_w+TMin_w=-2^{w-1}+(-2^{w-1})=-2^w
TMinw+TMinw=−2w−1+(−2w−1)=−2w
因为−2w<−2w−1,发生负溢出,所以得到结果会加上2w,如下式: 因为-2^w<-2^{w-1},发生负溢出,所以得到结果会加上2^w,如下式: 因为−2w<−2w−1,发生负溢出,所以得到结果会加上2w,如下式:
TMinw+TMinw的逆元=−2w+2w=0因此TMinw逆元是其本身 TMin_w+TMin_w的逆元=-2^w+2^w=0 \\ 因此TMin_w逆元是其本身 TMinw+TMinw的逆元=−2w+2w=0因此TMinw逆元是其本身
10.4 乘法运算
-
无符号数乘法
w 位的无符号数 x 和 y,二者的乘积可能需要 2w 位来表示。在 C 语言中,定义了无符号数乘法所产生的结果是 w 位,因此,运行结果会截取 2w 位中的低 w 位。截断采用取模的方式,因此,运行结果等于 x 与 y 乘积并对 2 的 w 次方取模
-
补码乘法
计算机的有符号数用补码表示,因此补码乘法就是有符号数乘法。无论是无符号数 乘法,还是补码乘法,运算结果的位级表示都是一样的,只不过补码乘法比无符号 数乘法多一步,需要将无符号数转换成补码(有符号数)。
虽然完整的乘积结果的位级表示可能会不同,但是截断后的位级表示都是相同的
注:如果乘以的是2的整倍数,可以通过左移进行快速计算。
例如:chart 9(0000 1001),左移一位变为18(00010010),等于9×29\times29×2
又例如:x×14=x⋅(24−21)=(x<<4)−(x<<1)x\times14=x\cdot(2^4-2^1)=(x<<4)-(x<<1)x×14=x⋅(24−21)=(x<<4)−(x<<1)
10.5 除以2的倍数也可以用移位运算实现
-
无符号数采用逻辑右移
-
有符号数(补码)采用算术右移
- 当补码小于0,需要先加上偏置(偏执量为2k−12^k-12k−1)再进行算术右移
- 当补码大于0,可以直接进行算术右移
除法结果总是向0舍入,如3.14→33.14\rightarrow33.14→3,3.14→33.14\rightarrow33.14→3
11 浮点数
11.1 IEEE浮点数表示
V=(−1)s×M×2E V=(-1)^s\times M\times2^E V=(−1)s×M×2E
sss:符号位; EEE:阶码; MMM:尾数
例如:C语言中float为4个byte,占32bits,这32个bits划分如下:
最高位31为符号位(s=0为正数,s=1为负数)。23-30位这八位与阶码E的值有关,剩余23位与尾数M相关。
IEEE浮点表示可以分为一下三种
- 当阶码字段不全为0,且不全为1,此时表示规格化的值
- 当阶码字段全为0,此时表示非规格化的值
- 当阶码字段全为1,表示特殊值(无穷或“不是一个数,NaN”)
11.2 整形转单精度浮点型
例如整形12345,对应二进制为[0000 0000 0000 0000 0011 0000 0011 1001],前18位没有意义,只看后14位,即11 0000 0011 1001。其中第一位为符号位,看后13位,由于单精度的小数字段长度位23,需要在后面补10个0,即[1 0000 0011 1001 0000 0000 00],这个二进制数即为单精度浮点数的小数字段。
从12345 的规格化表示可以发现阶码 E 的值等于 13,由于单精度浮点数的 bias 等于 127,因此根据公式 E=e-bias,可以计算出 e 的值等于 140,其二进制为1000 1100,这个二进制数即为浮点数的阶码字段。
最后再加上符号位的0,就可以推出来整个单精度浮点数的二进制数字。
11.3 舍入
- 向偶舍入
- 向零舍入
- 向下舍入
- 向上舍入