1.语言
1.1 机器语言
人和人沟通的桥梁:语言
人与计算机打交道 --> 学习计算机的语言 --> 什么是机器语言
#我们目前主流的电子计算机! 状态: 0 和 1 #最早的程序员:穿孔卡带! 加 0100 0000 减 0100 1000 乘 0100 1000 0100 1000 除 0100 1000 1100 1000
1.2 汇编语言
这些复杂的机器语言的简化 --> 助记符:汇编语言 --> 人能够理解的语言转换成为机器能够理解的语言
加 INC -编译器-> 0100 0000 减 DEC 0100 1000 乘 MUL 0100 1000 0100 1000 除 DIV 0100 1000 1100 1000
离程序的本质:隔阂
汇编一般用于底层的编写,单片机…
1.3 C语言
加 A+B -编译器-> 0100 0000 减 A-B 0100 1000 乘 A*B 01001000 0100 1000 除 A/B 0100 1000 1100 1000
1.4 学习思路
2.进制
2.1 学习进制的障碍
- 十进制 --> 人类天然的选择就是十进制,十个指头 --> 跳出固有思维的方法
- 每一种进制都是完美的,都有自己的计算方式
2.2 进制类型
- 1进制
逢一进一,结绳记事
-2进制
逢二进一,计算机
-8进制
逢八进一,8个符号组成:0 1 2 3 4 5 6 7
-10进制
逢十进一,10个符号组成:0 1 2 3 4 5 6 7 8 9
-16进制
逢十六进一,16个符号组成:0 1 2 3 4 5 6 7 8 9 a b c d e f
进制并没有那么复杂,以查数为例
# 一进制 1 1 1 1 1 1 1 1 1 1 ...... #二进制 0 1 10 11 100 101 110 111 1000 1001 # 三进制 0 1 2 10 11 12 20 21 22 100 101 102 110 111 112 120 121 122 ...... # 七进制 0 1 2 3 4 5 6 10 11 12 13 14 15 16 20 21 22 23 24 25 26 ...... 60 61 62 63 64 65 66 100 101 102 103 104 105 106 ......
2.3 进制的本质
- 就是一组符号,逢几进几
- 真实十进制:0 1 2 3 4 5 6 7 8 9 --> 10
我的十进制:0 2 4 6 a o e f k q --> 20 (对应真实的10) - 加密解密:程序员 —— 破解程序的人 --> 进制的加密
数字量一大,总是有规律的 - 问题: 1 + 1 = 3 是对的吗? --> 用进制来回答这个问题
3.进制怎么运算
运算的本质就是 查数
八进制表:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
八进制的乘法表:
1 * 1 = 1 | 1 * 2 = 2 | 1 * 3 = 3 | 1 * 4 = 4 | 1 * 5 = 5 | 1 * 6 = 6 | 1 * 7 = 7 |
2 * 2 = 4 | 2 * 3 = 6 | 2 * 4 = 10 | 2 * 5 = 12 | 2 * 6 = 14 | 2 * 7 = 16 | |
3 * 3 = 10 | 3 * 4 = 14 | 3 * 5 = 17 | 3 * 6 = 22 | 3 * 7 = 25 | ||
4 * 4 = 20 | 4 * 5 = 24 | 4 * 6 = 30 | 4 * 7 = 34 | |||
5 * 5 = 31 | 5 * 6 = 36 | 5 * 7 = 43 | ||||
6 * 6 = 44 | 6 * 7 = 52 | |||||
7 * 7 = 61 |
八进制的加法表:
1 + 1 = 2 | ||||||
1 + 2 = 3 | 2 + 2 = 4 | |||||
1 + 3 = 4 | 2 + 3 = 5 | 3 + 3 = 6 | ||||
1 + 4 = 5 | 2 + 4 = 6 | 3 + 4 = 7 | 4 + 4 = 10 | |||
1 + 5 = 6 | 2 + 5 = 7 | 3 + 5 = 10 | 4 + 5 = 11 | 5 + 5 = 12 | ||
1 + 6 = 7 | 2 + 6 = 10 | 3 + 6 = 11 | 4 + 6 = 12 | 5 + 6 = 13 | 6 + 6 = 14 | |
1 + 7 = 10 | 2 + 7 = 11 | 3 + 7 = 12 | 4 + 7 = 13 | 5 + 7 = 14 | 6 + 7 = 15 | 7 + 7 = 16 |
# 用八进制计算下面结果 277 + 333 = 632 276 * 54 = 20250 237 - 54 = 163 # 减法的本质就是加法 --> 237 + (-54) 234 / 4 = 47 # 除法的本质:除数乘以那个数最接近的结果即可
结论:无论是什么进制,本身都是有一套完美的运算体系的,我们都可以通过列表的方式将它计算出来
4.二进制
为什么学习理解二进制:
- 计算机使用二进制
- 寄存器、内存、位,底层的每一位都是有含义的 --> 汇编入门理解的基础
硬操作:达到极限了 --> 软操作的提升:追求语言的极限——并发语言
电子计算机:
# 二进制: 0 和 1 0 1 10 11 100 101 110 111 1000 1001 1010 1011 1100 1101 1110 1111 # 二进制这么写并表示是很麻烦的 --> 能否简化二进制 --> 用十六进制表示 0 1 2 3 4 5 6 7 8 9 a b c d e f
5.数据宽度
计算机内存是有限制的 --> 给数据增加数据宽度
5.1 强类型语言
C,C++,Java都需要定义数据的类型 --> 计算机底层需要我们给这些数据定义宽度
单位 | 大小 |
位(bit) | 0 - 1 |
字节(byte) | 0 - 0xFF |
字(word) | 0 - 0xFFFF |
双字(dword) | 0 - 0xFFFFFFFF |
在计算机中,每个数据都需要给它定义类型 --> 即给它定义宽度,在内存中的宽度
5.2 弱类型语言
弱类型语言不需要去定义类型,直接 let var 定义,不需要区分类型 --> 这是因为编译器帮我们做了
6.有符号数和无符号数
6.1 无符号数规则
你这个数字是什么,那就是什么
二进制数: 1 0 0 1 1 0 1 0 转成十六进制:0x9A 转成十进制:154
6.2 有符号数规则
最高位是符号位:1代表负数,0代表正数
二进制数: 1 0 0 1 1 0 1 0 --> 如何进行转换 --> 涉及到原码、反码和补码
7.原码反码补码
有符号数的编码规则
7.1 原码
最高位为符号位,其余位 等于 这个数的绝对值的对应位
7.2 反码
- 正数:反码和原码相同
- 负数:符号位一定是1,其余位对原码取反
7.3 补码
- 正数:补码和原码相同
- 负数:符号位一定是1,反码 + 1
1 #原码 0 0 0 0 0 0 0 1 #反码 0 0 0 0 0 0 0 1 #补码 0 0 0 0 0 0 0 1 -1 #原码 1 0 0 0 0 0 0 1 #反码 1 1 1 1 1 1 1 0 #补码 1 1 1 1 1 1 1 1 -7 #原码 1 0 0 0 0 1 1 1 #反码 1 1 1 1 1 0 0 0 #补码 1 1 1 1 1 0 0 1 十进制加法: 3 + 5 #转成二进制运算 1 0 1 0 1 -------- 1 0 0 0
如果看到一个数字,二进制的,需要先了解它是有符号数还是无符号数。
计算机的存储是按照补码的方式来存储的
8.位运算
计算机现在是可以存储所有的数字(整数,浮点数,字符)的。
8.1 为什么学习位运算
很多底层的调试器需要通过 位 来判断CPU的状态
8.2 位运算分类
8.2.1 与运算( and & )
全 1 为 1,否 则 为 0
#与运算 1011 0001 1101 1000 ---------- 1001 0000
8.2.2 或运算( or | )
全 0 才 0,否 则 为 1
#或运算 1011 0001 1101 1000 ---------- 1111 1001
8.2.3 异或运算( xor ^ )
不 同 为 1,相 同 为 0
#异或运算 1011 0001 1101 1000 ---------- 0110 1001
8.2.4 非运算( 单目运算符 not ~)
0 变 1,1 变 0
#非运算 1011 0001 ---------- 0100 1110
8.2.5 位运算(移动位)
左移:shl <<
高 位 溢 出,低 位 补 0
#所有二进制位全部左移若干位,高位就丢弃了,低位补0 0000 0001 #左移一位 0000 0010
右移:shr >>
低位移除,符号位与高位一致
#所有二进制位全部右移若干位,低位就丢弃了,高位与符号位一致 1000 0001 #右移两位 1110 0000 int a = -1; printf("%d\n",a>>2);
二进制、位运算 --> 加减乘除
9.位运算实现加减乘除
计算机只认识 0 和 1
基本数学运算是建立在 加减乘除 基础上 --> 探究玩计算机加减乘除会发现 计算机只会做加法
9.1 加法
4 + 5 --> #十进制转成二进制运算 0000 0100 0000 0101 ---------- 0000 1001 #以上加法过程,计算机是不会直接加的,这是我们人的计算方式
那么计算机是怎么操作的?
#计算机的实现原理 0000 0100 0000 0101 #第一步:异或,如果不考虑进位(即当异或出的结果全为1的时候),则可以直接出结果 0000 0100 0000 0101 ----------- 异或 0000 0001 #第二步:与运算,判断进位,如果与运算结果全为0,说明没有进位 0000 0100 0000 0101 ----------- 与 0000 0100 #第三步:将与运算的结果,左移一位,因为与运算结果中如果出现了1,说明有进位 0000 0100 --> 左移一位 0000 1000 #第四步:将 第一步的异或结果 与 第三步的移位结果 进行异或 0000 0001 0000 1000 ------------ 异或 0000 1001 #第五步:对 第四步用来异或的两个数 进行与运算,目的是进行结果的确定(判断进位,如果与运算结果全为0,说明没有进位) 0000 0001 0000 1000 ------------ 与 0000 0000 #因此最终结果就是与运算为0的结果的上一个异或结果
9.2 减法
4 + (-5) -5 --> 原码: 1000 0101 --> 反码: 1111 1010 --> 补码: 1111 1011 #因为在计算机底层,减法本质就是加法运算,因此运算步骤和加法一样 #第一步:异或,如果不考虑进位(即当异或出的结果全为1的时候),则可以直接出结果 0000 0100 1111 1011 ---------- 异或 1111 1111 #全为1,说明没有进位,则可以直接出结果 #第二步:与运算,目的是进行结果的确定(判断进位,如果与运算结果全为0,说明没有进位)【对应上面的第五步】 0000 0100 1111 1011 ----------- 与 0000 0000 #因此结果就是 1111 1111,变成16进制就是ff,变成10进制就是-1
9.3 乘法
x * y ,就是 y个x相加,因此本质还是加法
9.4 除法
x / y ,本质就是减法,就是 x 中最多能够减去多少个 y,而减法的本质还是加法
因此得出结论:计算机只会做加法
机器语言就是位运算,都是电路来实现的 --> 这就是计算机最底层的本质
通过机器语言可以来实现加法计算器,设计电路…
10.汇编语言环境说明
通过 指令 来代替我们的二进制编码
- 通过汇编指令可以给计算机发一些操作,然后让计算机执行。
- 编译器的发展,让我们远离了底层。但真正底层的大佬,几乎都是最原始的IDE。
- 在学习汇编前,需要先掌握环境的配置:
- Vc6(程序到汇编的理解)
- OD(反编译工具)
- 抓包工具
- 加密解密工具
学汇编不是为了写代码,是为了 理解程序的本质
汇编入门:了解汇编和程序的对应关系,程序的本质即可
11.通用寄存器
寄存器:
存储数据:CPU > 内存 > 硬盘
32位CPU:寄存器有 8 16 32 位
64位CPU:寄存器有 8 16 32 64 位
通用寄存器:可以存储任何东西
32位的通用寄存器只有8个,存值的范围 0 - FFFFFFFF,对于二进制来说,直接修改值
计算机如何向 寄存器 存值 --> mov指令
mov 存的地址,存的数 mov 存的地址1,存的地址1
可以将数据写入到寄存器,可以将寄存器中的值写到寄存器
计算机的核心:计算力
不同的寄存器:
32位(FFFFFFFF) | 16位(FFFF) | 8位(FF) |
EAX | AX | AL |
ECX | CX | CL |
EDX | DX | DL |
EBX | BX | BL |
ESP | SP | AH |
ENP | NP | CH |
ESI | SI | DH |
EDI | DI | BH |
注意:八位的寄存器,L低8位,H高八位
除去这些通用寄存器之外,其他的寄存器每一位都有自己特定的功能
12.内存
寄存器很小,不够用 --> 数据放到内存 --> 会买的内存条
在32位操作系统中,每个应用程序进程都有4GB的内存空间,但这是空头支票
程序真正运行的时候,才会用到物理内存。
1B = 8bit
1KB = 1024B
1MB =1024KB
1GB = 1024MB
4G的内存,4096MB --> 最终计算为位,就是这个可以存储的最大容量。
内存地址存一个数 --> 占用的大小,数据宽度,存到哪里?
计算机中内存地址很多,空间很大 --> 就需要给每个空间分配一个地址 / 名字。
这些给内存起的编号,就是我们的内存地址。
32位8个16进制的值。32位:寻址能力是4GB。
FFFFFFFF+1 = 100000000,最大的值。
位是怎么限制内存大小的。
100000000内存地址*8 --> 位:800000000 --> 转换为10进制/8 --> 4,294,967,296字节
按照规则/1024,最终发现就是4GB
所以每个内存地址都有一个编号。
数据宽度: byte word dword
不是任意的地址都可以写东西的,申请使用的。只有程序申请过的内存地址我们才可以使用。
#汇编如何向内存中写值。 mov 数据宽度 内存地址,1 #例如:在0x19FF70的内存地址存放一个数据宽度为byte的值为4 mov byte ptr ds : [0x19FF70],4 --> 传递的值的大小一定要和数据宽度相等
内存地址有多种写法
- ds : [ 0x19FF70 + 4 ] 内存地址偏移
- ds : [ eax ] 将寄存器的值写入到内存
- ds : [ eax + 4 ] 寄存器偏移
- 数组底层
- ds : [ reg + reg * { 1 , 2 , 4 , 8 } ]
- ds : [ reg + reg * { 1 , 2 , 4 , 8 } + 4 ] 偏移