博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
函数调用过程小白文了解一下
阅读量:4110 次
发布时间:2019-05-25

本文共 1541 字,大约阅读时间需要 5 分钟。

函数调用小白文了解一下

基础理论

大多数人都知道,一个函数的调用会发生压栈,EIP的变化,返回时会出栈和还原EIP,但是当前环境下大多学习编程的人没有学习过汇编语言,所以我这里用小白文的方式让大家理解具体发生了什么。

说用小白文,就用小白文,但是还是需要说一些基础知识。这里不会写出所有寄存器,因为毕竟要用小白文。不然没接触过汇编的会被各个寄存器搞懵逼。

  • EBP 栈的基址指针,也就是栈底
  • ESP 指向栈顶
  • EIP 指向CPU下一次运行的地址

每个方法有自己的栈,线程有自己的线程栈,进程有自己的进程栈,这里不说线程/进程内核切换栈压/出过程,只说一下普通的函数调用过程。

大家也都知道递归会出现栈溢出,就是因为栈的大小是有限的,你递归一次就要进行压栈,压到不够用了不就挂了?

关于栈的概念相信所有程序员都懂,不过这里要说的栈可能和你想象的不太一样,因为这个栈是栈顶移动,栈底不动,压栈栈顶做减法 ,大家试着把这个栈想象成古人写字一样,从右到左。每次新的函数调用就另起一行从头写。

栗子

举个栗子 下面的代码

void main(){
call(1);}void call(int v){
//todo something return;}

在调用call(1)的时候,分为下面几个步骤:

  • 将1压入栈 (如果是变量就是将地址压入栈,esp-4 x86下指针地址就是一个4字节)
  • call xxxx 记录当前eip的下一行地址(当前地址的下一行,不是call地址的下一行,所以又要做减法咯 esp-4)
  • 然后当前 EIP 会指向call的函数地址(让CPU下一次执行从CALL函数的地址开始)
  • 然后进入call开始执行
  • 压入ebp(esp 又要-4 存放ebp,此时esp+4 的话就会找到返回的eip地址,+8就是那个1了),将esp的值赋给ebp(因为每个函数有自己的栈),现在做的就是将栈初始化,栈顶栈底在同一个地址。(这里是两步我合成一步写好理解)。
  • 做了一些事情
  • ret 从esp中取出之前保存的值,赋值给eip
  • 程序跳转回刚才保存的地址(eip)
    因为默认的__cdecl,这种方式是调用者自身平栈,所以你在这个进入call后的汇编代码中看不见负责清理压入的参数(1)的汇编代码。
    正好说一下如果是__stdcall的情况下你可能会看见ret x 这个x就是被调用者(进入的这个call)负责平栈。

文字看着累,来看个图:

在这里插入图片描述
最简单的理解就是,在执行call以前,因为要跳转到call的地址,call地址和调用它的地址可能相隔很远,所以我必须先保存当前位置的下一行的地址,保存好了之后才会跳转过去执行,执行完了之后取出之前保存的值,让程序跳转回来,继续从刚才保存的地址开始走。

上面的例子是个x86下的汇编指令,如果是x64程序指针地址就会是8字节大小,如果你是在看AT&T的汇编指令,需要区分一下mov %esp,%ebp,AT&T的汇编指令你可以理解为按顺序的阅读,更适合阅读,像这样:

  • AT&T mov %esp,%ebp
  • Intel mov ebp,esp
    两者的意思都是将esp赋值给ebp。

事实上目前的编译器优化,会优化成和你想象的不太一样的情况,比如,memcpy一个"aaaa",它可能直接优化成*指针=aaaa的ascii码,因为aaaa刚好占4字节且是常量,他会直接优化成指针赋值.

代码的长短

很多没有接触过编译器或汇编的小伙伴,可能会认为,代码越短,效率越高,事实上并非如此,无论如何都会出现寄存器保存临时值,有时候长的代码和一行代码效率是相同的,所以代码通俗易懂才是硬道理,不要硬凑成一行,读起来令人找不到头,毕竟代码是给人看的。

转载地址:http://sxlsi.baihongyu.com/

你可能感兴趣的文章
查找最大值最小值
查看>>
C#中ColorDialog需点两次确定才会退出的问题
查看>>
数据库
查看>>
nginx反代 499 502 bad gateway 和timeout
查看>>
linux虚拟机安装tar.gz版jdk步骤详解
查看>>
python猜拳游戏
查看>>
python实现100以内自然数之和,偶数之和
查看>>
python数字逆序输出及多个print输出在同一行
查看>>
ESP8266 WIFI数传 Pixhaw折腾笔记
查看>>
苏宁产品经理面经
查看>>
百度产品经理群面
查看>>
去哪儿一面+平安科技二面+hr面+贝贝一面+二面产品面经
查看>>
element ui 弹窗在IE11中关闭时闪现问题修复
查看>>
vue 遍历对象并动态绑定在下拉列表中
查看>>
Vue动态生成el-checkbox点击无法选中的解决方法
查看>>
python __future__
查看>>
MySQL Tricks1
查看>>
python 变量作用域问题(经典坑)
查看>>
pytorch
查看>>
pytorch(二)
查看>>