已 c++ 为例 😐 ---------- 🙄
定义变量
也就是说单纯的定义一个变量,不给它初始化,系统认为这个变量是没有意义的,就决定不会为它分配内存空间所以自然也不会有对应的汇编代码。或者可以理解为编译器暂时把你定义的变量记录下来但没有通知系统去做一些别的事情。
但是当定义一些被 c++ 封装后的结构类型时,就不一样了:
没错,string 也是! 😐
变量初始化
先来看 bool 类型的初始化:
将 false 对应的值存入变量 b 的地址中,没毛病 😀,如果把 false 改为 true 就是 1:
可见 false 其实就是 0x0,true 就是 0x1
再来看 char 和 unsigned char 的初始化:
‘a’ 对应了 acsii 码的 0x61,所以相当于把 acsii 码存入变量的地址,如果是正数则 char 和 unsigned char 没有什么区别。但事实上就算用负数初始化 char 和 unsigned char,它们的值也是相同的:
再看宽字节的初始化:它使用 eax 做了一个中转,然后再把值存入变量的地址中。
string 类型:可以理解为先在某个内存里写入一串字符,然后下面的 call 会从这个地址里取值,然后给 s 赋值。
对于正数来讲,int 和 unsigned int 没有区别:
同样和 char / unsigned char 一样,用负数赋值这两个变量也会得到相同的汇编指令结果:
所以可以得出结论:unsigned 只是会改变显示,不会改变数值本身
float 的初始化就比较奇怪了:
movss:是将一个单精度数传输到 xmm 寄存器的低32位
xmm:专门做高精度浮点值运算,有8个分别为 xmm0 ~ xmm7
double 和 float 基本一样:
short 需要用 eax 做一下中转,而 long 和 longlong 都是直接赋值:
指针类型:nullptr 被当作 0 处理,对指针进行赋值使用 lea指令,rax 寄存器作为中转,最后由 rax 赋值给 p。
结构体:结构体对象地址也存放在基地址寄存器(rbp)中,然后通过成员在结构体中对应的偏移进行访问。
那么问题来了,直接再定义的时候赋值和先定义再赋值的汇编代码一样么?
答案是肯定的,因为我们上面已经验证了值定义一个变量不初始化是不会有对应的汇编代码的:
初始化一个数组:
rbp:基址指针寄存器,用于提供堆栈内某个单元的偏移地址
这里其实我们就可以推断出 a[] 的基地址是 rbp+18h 了 (也就是a[0] 的地址)
初始化一些特殊结构
其实也是调用了封装好的功能,主要是执行 call 指令:
输出 / 输入
同样也是调用 call 来实现:
调用函数
参数从右向左入栈,前4个参数存放在 rcx,rdx,r8,r9 中,之后的参数由 rax 作为中转存放在堆栈(rsp)中:
调用 API,依然遵从之前的参数入栈顺序,没由什么区别:
逻辑运算
+:将变量存入寄存器,使用 add 指令相加再存入寄存器,最后寄存器的值存入变量地址
-:和+ 相同,但指令变成 sub
*:第一个操作数存入寄存器,调用 imul 指令完成有符号乘法并将结果存入寄存器,最后把寄存器的值存入变量地址中。
imul :有符号乘法指令
mul:无符号乘法指令
/:第一个操作数存入寄存器,调用 idiv 指令完成有符号除法并将结果存入寄存器,最后把寄存器的值存入变量地址中。
idiv:有符号除法
div:无符号除法
%:除法指令前多一个 cdq 指令
cdq:将一个32位有符号数(eax)扩展为64位有符合数(edx)
++: inc 累加指令
- -: dec 累减指令
连等:cmp 和 jne
不等: cmp 和 je
> 和 <: jle 和 jge
大于等于和小于等于:jl 和 jg
&&:两个 cmp 和 两个 je
||:两个 cmp 和 两个 jne
&: and
|:or
^: xor
~:not
==<< 和 >> ==: shl 和 sar
movzx:专门用来移动寄存器的低位(l)或高位(h)
分支语句
if: if 关键字并没有做什么特殊操作,真正发挥作用的还是 cmp 和 jne (由 == 号产生):
else:jmp 保证执行了 if 就一定不会执行 else ,反之亦然:
switch:break 其实就是一个 jmp,另外有几个 case 就有几对 cmp ,je(对应了 != 号对应的汇编指令),switch 中的值会首先存入 eax 寄存器:
循环语句
for:首先初始化 i = 0,存入 基址寄存器(rbp) 中,然后用 cmp+jge(大于等于则跳转)判断是否要结束循环,如果不结束则使用 inc 让 i 自增:
jge:大于等于跳转
jg:大于跳转
jle:小于等于跳转
jl:小于跳转
while:基本一个套路,就是指令的顺序变化了一点
do while:do while 和 while/for 有不同之处,同样用高级语言做小于判断,do while 使用 cmp + jl,也就是说 while/for判断什么时候 “结束”,而 do while 则判断什么时候 “还可以运行”:
遍历数组:i 存入基地址寄存器 [rbp+0x34], 访问数组时,先将 i的值存入 rax 寄存器,然后使用 rax*4 的方式访问数组中的值(这里因为 int 类型所以是 *4 ,如果是char 数组则是 *1,以此类推…):
原文链接: https://www.cnblogs.com/csnd/p/15613470.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;
也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/355138
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!