汇编语言入门学习

前言

之前看过些汇编,做了笔记,但是不够好,这次看课再重新记录一遍
MJ大神真的是大神
利用汇编挖掘编程语言的本质

程序的本质

软件\程序的执行过程
在这里插入图片描述

在这里插入图片描述

寄存器与内存

寄存器的运算速度快
好多操作都是,内存中的数据放在寄存器计算,计算完后再放回到内存中
即:
通常,CPU会先将内存中的数据存储到通用寄存器中,然后再对通用寄存器中的数据进行运算


举个栗子:

假设内存中有块红色内存空间的值是3,现在想把它的值加1,并将结果存储到蓝色内存空间
在这里插入图片描述
比如以下两句代码:

int a = 3;
int b = a + 1;

其过程大致为:

  1. CPU首先会将红色内存空间的值放到EAX寄存器中:mov eax, 红色内存空间
    在这里插入图片描述
  2. 然后,让EAX寄存器与1相加:add eax, 1
    在这里插入图片描述
  3. 最后将值 赋值给蓝色内存空间:mov 蓝色内存空间, eax

在这里插入图片描述


编程语言的发展

  1. 机器语言:由0和1组成
  2. 汇编语言:用符号代替了0和1,比机器语言便于阅读和记忆
  3. 高级语言:就是现在的语言比如C\C++\OC\JAVA等

比如1+1这个代码:
机器语言可能就是:010100101
汇编语言大概就是:mov eax, 1 add eax, 1
高级语言就是平时写的:1 + 1

在这里插入图片描述

  • 汇编语言机器语言一一对应,每一条机器指令都有与之对应的汇编指令

  • 也就是,通过汇编语言可以得到机器语言;通过机器语言也可以得到汇编语言

  • 高级语言可以通过编译得到汇编语言

  • 但是,通过汇编语言或者机器语言 几乎不可能还原得到高级语言

在这里插入图片描述

  • 编译型语言(不依赖虚拟机):C\C++\OC\Swift
  • 编译型语言(依赖虚拟机):Java\Ruby
  • 脚本语言:JS\Python\PHP

编译器分:前端、后端
Clang属于前端编译器

sizeof()是编译器特性
编译器看到sizeof(int)就直接当4处理了


代码执行效率分析

问:if-else和switch,谁的效率高?

使用if-else和switch分别写一段判断代码,如下:

在这里插入图片描述

在这里插入图片描述

汇编语言语句部分解析:

001718F8 mov dword ptr[no], 4
移动4到ptr[no]上
对应:int no = 4

001718FF cmp dword ptr[no], 1
cmp 就是 compare比较
比较 ptr[no]与1的值
对应:if(no == 1){

00171903 jne main + 44h(0171914h)
最后的h是16进制的意思
j开头的,一般都是jump,跳转语句
jne = jump not equal不相等
跳到00171914
00171914的内容是:
00171914 cmp dword ptr[no], 2
其意思是:if(no == 2){

如果相等,则不跳,继续执行下一句

call调用
会将下一条语句的地址,放入栈里面

jmp
jmp == jump
无条件跳转,看到jmp,就执行跳转操作

je
je == jump equal
如果相等则跳转

通过汇编代码分析,可以得出:
if-else是将找到对应条件之前所有的if-else条件都进行对比
if-else最好将命中率比较高的写在前面

switch是将要找的结果直接算出来,然后直接跳到对应的case里面

如果判断条件比较多,而且命中的值在后面,if-else会一步一步比较到后面,而switch可以直接算出在哪里case,因此,switch在某些时候比if-else的效率要好一点


通过减少判断条件,分析switch汇编代码发现:
如果判断比较少,使用switch也是跟if-else判断差不多,是一个一个比较得出结果的

0104195E sub ecx, 1
sub是减法的意思
等价于:ecx = exc - 1

jmp 01041976h是直接跳转到01041976地址
jmp [01041976h]是先取出01041976h里面的地址值B,再跳转到B的地址(相当于指针)

在C++中
a++ = 4;
++a = 4;✔️


构造函数

构造函数(也叫构造器),在对象创建的时候自动调用,一般用于完成对象的初始化工作

好多书上会有:
默认情况下,编译器会为每一个类生成空的无参的构造函数
但,其实是错误的❎
在某些特定的情况下,编译器才会为类生成空的无参的构造函数,比如:

  • 成员变量在声明的同时进行了初始化
  • 包含了对象类型的成员,且这个成员有构造函数(编译器生成或自定义)
  • 父类有构造函数(编译器生成或自定义)

也就是,对象创建后,需要做一些额外操作时(比如内存操作、函数调用),编译器一般会为其自动生成构造函数

class Car{
	public:
	int m_price = 5;
}

等价于

class Car{
	public:
	int m_price;
	Car(){
		m_price = 5;
	}
}

函数的内存布局

调用一个函数,会开辟一段栈空间给函数

学习之前,认识两个寄存器:esp、ebp
这两个寄存器,被称为:栈指针寄存器

esp:指向栈顶

test(1, 2)
转换为汇编,里面会有:

push 2
push 1

将2push到栈
将1push到栈

也就是,函数里面的实参,被放在栈里面了

分配给函数的内存空间多大,是由编译器决定的(会根据函数里面参数、局部变量的数量不同,分配大小也不同)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值