CPU指令解析及函数调用机制

目录

一、CPU指令解析

最常用的mov指令

对栈进行push和pop

二、函数的调用机制


一、CPU指令解析

最常用的mov指令

        指令中最常使用的是对寄存器和内存进行数据存储的 mov 指定数据的存储地和读出源。操作数中可以指定寄存器、常数、标签(附加在地址前),以及用方括号([ ])围起来的这些内容。如果指定了没有用([ ])方括号围起来的内容,就表示对该值进行处理;如果指定了用方括号围起来的内容,方括号的值则会被解释为内存地址,然后就会对该内存地址对应的值进行读写操作。让我们对上面的代码片段进行说明指令,mov指令的两个操作数,分别用来

mov
ebp,espeax,dword ptr [ebp+8]

        mov ebp,esp中,esp 寄存器中的值被直接存储在了ebp中,也就是说,如果esp 寄存器的值是100的话那么 ebp 寄存器的值也是100.

        而在 mov eax,dword ptr [ebp+8]这条指令中,ebp寄存器的值+8后会被解析称为内存地址。如果 ebp

        寄存器的值是100的话,那么eax寄存器的值就是100+8的地址的值。dword ptr 也叫做double word pointer 简单解释一下就是从指定的内存地址中读出4字节的数据

对栈进行push和pop

        程序运行时,会在内存上申请分配一个称为栈的数据空间。栈(stack)的特性是后入先出,数据在存储时是从内存的下层(大的地址编号)逐渐往上层(小的地址编号)累积,读出时则是按照从上往下进行读取的。

栈的模型:

c97f85631e1f4e1e904339357844ca21.png

        栈的存储临时数据的区域,它的特点是通过 push 指令和pop指令进行数据的存储和读出。向栈中存储数据称为 入栈 ,从栈中读出数据称为 出栈 ,32位 x86 系列的CPU中,进行1次push或者pop,即可处理32位(4字节)的数据

二、函数的调用机制

        下面我们一起来分析一下函数的调用机制,我们以上面的C语言编写的代码为例。首先,让我们从MyFunc 函数调用 AddNum 函数的汇编语言部分开始,来对函数的调用机制进行说明。栈在函数的调用中发挥了巨大的作用,下面是经过处理后的MyFunc函数的汇编处理内容

_MyFunc    proc    near                            
 push       ebp     ; 将ebp寄存器的值存入栈中         (1)
 mov        ebp,esp ; 将esp寄存器的值存入ebp寄存器中  (2)
 push       456     ; 将456入栈                      (3)
 push       123     ; 将123入栈                      (4)
 call       _AddNum ; 调用 AddNum 函数               (5)
 add        esp,8   ; esp寄存器的值 + 8              (6)
 pop        ebp     ; 读出栈中的数值存入esp寄存器中    (7)
 ret                ; 结束 MyFunc 函数,返回到调用源   (8)
_MyFunc    ebp 

        代码解释中的(1)、(2)、(7)、(8)的处理适用于C语言中的所有函数,我们会在后面展示 AddNum函数处理内容时进行说明。这里希望大家先关注(3)-(6)这一部分,这对了解函数调用机制至关重要。

        (3)和(4)表示的是将传递给AddNum函数的参数通过push入栈。在C语言源代码中,虽然记述为函数AddNum(123, 456),但入栈时则会先按照456, 123这样的顺序。也就是位于后面的数值先入栈。这是C语言的规定。(5)表示的call 指令,会把程序流程跳转到AddNum函数指令的地址处。在汇编语言中,函数名 表示的就是函数所在的内存地址。AddNum 函数处理完毕后,程序流程必须要返回到编号(6)这一行。call 指令运行后,call 指令的下一行(也就指的是(6)这一行)的内存地址(调用函数完毕后要返回的内存地址)会自动的push入栈。该值会在AddNum函数处理的最后通过ret指令pop出栈,然后程序会返回到(6)这一行。

        (6)部分会把栈中存储的两个参数(456和123)进行销毁处理。虽然通过两次的pop指令也可以实现,不过采用esp 寄存器+8的方式会更有效率(处理1次即可)。对栈进行数值的输入和输出时,数值的单位是4字节。因此,通过在负责栈地址管理的esp 寄存器中加上4的2倍8,就可以达到和运行两次 pop命令同样的效果。虽然内存中的数据实际上还残留着,但只要把esp 寄存器的值更新为数据存储地址前面的数据位置,该数据也就相当于销毁了

我在编译 Sample4.c 文件时,出现了下图的这条消息

D: \C> bcc32-c-S Sample4.cBorland C++ 5. 5. 1 for Win32 Copyright <c> 1993.2000
BorlandSample4.c:Warning W8004 Sample4.c 10: 'c' is assigned a value that is 
never used in function MyFunc

        上面的意思是指c的值在MyFunc定义了但是一直未被使用,这其实是一项编译器优化的功能,由于存储AddNum函数返回值的变量c在没有被用到,因此编译器就认为 该变量没有意义,进而也就没有生成与之对应的汇编语言代码

下图是调用AddNum这一函数前后栈内存的变化:

56bb968b2c574474be9d5a1caa1517bf.png

 

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏志121

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值