当前位置: 首页 > news >正文

如何做网站平台销售最新域名永久跳转

如何做网站平台销售,最新域名永久跳转,深圳有哪几个区,深圳seo优化公司哪家好From#xff1a;https://www.jianshu.com/p/594357dff57e C函数调用过程原理及函数栈帧分析#xff1a;https://blog.csdn.net/zsy2020314/article/details/9429707 从本篇开始#xff0c;我们讨论一些高级语言中的基础设施#xff1a;堆栈#xff0c;函数调用#xff0…  Fromhttps://www.jianshu.com/p/594357dff57e C函数调用过程原理及函数栈帧分析https://blog.csdn.net/zsy2020314/article/details/9429707 从本篇开始我们讨论一些高级语言中的基础设施堆栈函数调用变量生命周期等等话题。因为这里本身会涉及到比较多的汇编层面的基础概念。为了向大家说明汇编层的函数调用实现细节无奈我只能罗列出很多汇编上的概念因为本文假定读者不需要具有任何汇编知识。我讨厌长篇大论但本篇的解释可能仍然不够明晰。在此为自己知识的浅薄表示歉意。 1. 从代码的顺序执行说起 每一个程序员脑子里应该都有这么一种印象“程序是顺序执行的”。这个观点其实和我们开篇所讲的cpu的流水线执行过程直接相关。 让我们再回忆一下脑海中关于函数调用的概念也许会是这个样子 这里的“控制流转移”又是如何发生的呢在解释这个之前也许我们需要科普一点有关于汇编的知识。 2. 函数调用中的一些细节说明 2.1 函数调用中的关键寄存器 2.1.1 程序计数器PC 程序计数器是一个计算机组成原理中讲过的概念下面给出一个百度百科中的简单解释 程序计数器是用于存放下一条指令所在单元的地址的地方。 当执行一条指令时首先需要根据PC中存放的指令地址将指令由内存取到指令寄存器中此过程称为“取指令”。与此同时PC中的地址或自动加1或由转移指针给出下一条指令的地址。此后经过分析指令执行指令。完成第一条指令的执行而后根据PC取出第二条指令的地址如此循环执行每一条指令。 可以看到程序计数器是一个cpu执行指令代码过程中的关键寄存器它指向了当前计算机要执行的指令地址CPU总是从程序计数器取出当前指令来执行。当指令执行后程序计数器的值自动增加指向下一条将要执行的指令。 在x86汇编中执行程序计数器功能的寄存器被叫做EIP也叫作指令指针寄存器。 2.1.2 基址指针栈指针和程序栈 栈是程序设计中的一种经典数据结构每个程序都拥有自己的程序栈。很重要的一点是栈是向下生长的。所谓向下生长是指从内存高地址-低地址的路径延伸那么就很明显了栈有栈底和栈顶那么栈顶的地址要比栈底低。对x86体系的CPU而言其中 --- 寄存器ebpbase pointer 可称为“帧指针”或“基址指针”其实语意是相同的。 --- 寄存器espstack pointer可称为“ 栈指针”。 在C和C语言中临时变量分配在栈中临时变量拥有函数级的生命周期即“在当前函数中有效在函数外无效”。这种现象就是函数调用过程中的参数压栈堆栈平衡所带来的。对于这种实现的细节我们会在接下来的环节中详细讨论。 2.2. 堆栈平衡 堆栈平衡这个概念指的是函数调完成后要返还所有使用过的栈空间。这种说法可能有点抽象我们可以举一个简单的例子来类比 我们都知道函数的临时变量存放在栈中。那我们来看下面的代码它是一个很简单的函数,用来交换传入的2个参数的值 void __stdcall swap(int a,int b) {int c a;a b;b c; } 我们可以看到在这个函数中使用了一个临时变量int c;这个变量分配在栈中我们可以简单的理解为在声明临时变量c后我们就向当前的程序栈中压入了一个int值 int c a; push(a); //简单粗暴临时变量的声明理解为简单地向栈中push一个值。 那现在这个函数swap调用结束了我们是否需要退栈把之前临时变量c使用的栈空间返还回去需要吗不需要吗 我们假设不需要当我们频繁调用swap的时候会发生什么每次调用程序栈都在生长。直到栈满我们就会收到stack overflow错误程序挂掉了。 所以为了避免这种乌龙的事情发生我们需要在函数调用结束后退栈把堆栈还原到函数调用前的状态这些被pop掉的临时变量自然也就失效了这也解释了我们一直以来关于临时变量仅在当前函数内有效的认知。其实堆栈平衡这个概念本身比这种粗浅的理解要复杂的多还应包括压栈参数的平衡暂时我们可以简单地这样理解后面再做详细说明。 2.3. 函数的参数传递和调用约定 函数的参数传递是一个参数压栈的过程。函数的所有参数都会依次被push到栈中。那调用约定有是什么呢 C和C程序员应该对所谓的调用约定有一定的印象就像下面这种代码 void __stdcall add(int a,int b); 函数声明中的__stdcall就是关于调用约定的声明。其中标准C函数的默认调用约定是__stdcall,C全局函数和静态成员函数的默认调用约定是__cdecl类的成员函数的调用约定是__thiscall。剩下的还有__fastcall__naked等。 为什么要用所谓的调用约定调用约定其实是一种约定方式它指明了函数调用中的参数传递方式和堆栈平衡方式。 2.3.1 参数传递方式 还是之前那个例子swap函数有2个参数int a,int b。这两个参数入栈的顺序谁先谁后 其实是从左到右入栈还是从右到左入栈都可以只要函数调用者和函数内部使用相同的顺序存取参数即可。在上述的所有调用约定中参数总是从右到左压栈也就是最后一个参数先入栈。我们可以使用一份伪代码描述这个过程 push b; //先压入参数b push a; //再压入参数a call swap; //调用swap函数 其实从这里我们就可以理解为什么在函数内部不能改变函数外部参数的值因为函数内部访问到的参数其实是压入栈的变量值对它的修改只是修改了栈中的副本。指针和引用参数才能真正地改变外部变量的值。 2.3.2 堆栈平衡方式 因为函数调用过程中参数需要压栈所以在函数调用结束后用于函数调用的压栈参数也需要退栈。那这个工作是交给调用者完成还是在函数内部自己完成其实两种都可以。调用者负责平衡堆栈的主要好处是可以实现可变参数关于可变参数的话题在此不做过多讨论。如果可能的话我们可以以一篇单独的文章来讲这个问题因为在参数可变的情况下只有调用者才知道具体的压栈参数有几个。 下面列出了常见调用约定的堆栈平衡方式 调用约定堆栈平衡方式__stdcall函数自己平衡__cdecl调用者负责平衡__thiscall调用者负责平衡__fastcall调用者负责平衡__naked编译器不负责平衡由编写者自己负责 2.4. 栈帧的概念从esp和ebp说起 为什么我们需要ebp和esp2个寄存器来访问栈这种观念其实来自于函数的层级调用函数A调用函数B函数B调用函数C函数C调用函数D... 这种调用可能会涉及非常多的层次。编译器需要保证在这种复杂的嵌套调用中能够正确地处理每个函数调用的堆栈平衡。所以我们引入了2个寄存器 1. ebp指向了本次函数调用开始时的栈顶指针它也是本次函数调用时的“栈底”这里的意思是在一次函数调用中ebp向下是函数的临时变量使用的空间。在函数调用开始时我们会使用 mov ebp,esp 把当前的esp保存在ebp中。2. esp它指向当前的栈顶它是动态变化的随着我们申请更多的临时变量esp值不断减小正如前文所说栈是向下生长的。3. 函数调用结束我们使用 mov esp,ebp 来还原之前保存的esp。 在函数调用过程中ebp和esp之间的空间被称为本次函数调用的“栈帧”。函数调用结束后处于栈帧之前的所有内容都是本次函数调用过程中分配的临时变量都需要被“返还”。这样在概念上给了函数调用一个更明显的分界。下图是一个程序运行的某一时刻的栈帧图 3. 汇编中关于“函数调用”的实现 上面铺陈了很多的汇编层面的概念后我们终于可以切回到我们本次的主题函数调用。 函数调用其实可以看做4个过程也就是本篇标题 压栈: 函数参数压栈返回地址压栈跳转: 跳转到函数所在代码处执行执行: 执行函数代码返回: 平衡堆栈找出之前的返回地址跳转回之前的调用点之后完成函数调用1. call指令 压栈和跳转 下面我们看一下函数调用指令 0x210000 call swap; 0x210005 mov ecx,eax; 我们可以把它理解为2个指令 push 0x210005; jmp swap; 也就是首先把call指令的下一条指令地址作为本次函数调用的返回地址压栈然后使用jmp指令修改指令指针寄存器EIP使cpu执行swap函数的指令代码。 2. ret指令 返回 汇编中有ret相关的指令它表示取出当前栈顶值作为返回地址并将指令指针寄存器EIP修改为该值实现函数返回。 下面给出一组示意图来演示函数的返回过程 1. 当前EIP的值为0x210004指向指令ret 4程序需要返回 2. 执行ret指令将当前esp指向的堆栈值当做返回地址设置eip跳转到此处并弹出该值 经过这两步函数就返回到了调用处。 4. 从实际汇编代码看函数调用 4.1 程序源码和运行结果 源码 main.cpp#include stdio.hvoid __stdcall swap(int a, int b);int main(int argc, char* argv) {int a 1, b 2;printf(before swap: a %d, b %d\r\n, a, b);swap(a, b);printf(after swap: a %d, b %d\r\n, a, b); }void __stdcall swap(int a, int b) {int c a;a b;b c; } 程序运行结果 4.2 反汇编 可以看到在函数调用前函数参数已被压栈此时:EBP 00AFFCACESP 00AFFBBCEIP 00BF1853 我们按F11进入函数内部此时 其实就是call swap指令的下一条指令地址它就是本次函数调用的返回地址。 下面是一个swap函数的详细注释 当程序运行到 ret 8时 执行返回后 在返回前ESP 00AFFBB8返回后 ESP 00AFFBC4 0x00AFFBC4 - 0x00AFFBB8 0xC 这里的数值是字节数而我们知道int是4字节长度。所以0xC/4 3 正好是2个压栈参数一个返回地址。 4.3 调用堆栈 调试程序的时候我们经常关注的一个点就是VisualStudio显示给我们的“调用堆栈”功能这次让我们来仔细看一下它 我们重新执行一次程序这次我们关注一下vs显示的调用堆栈如下图 第一行是当前指令地址 第二行是外层调用者我们双击它跳转到如下地址 也许这也是为什么这个功能被叫做“调用堆栈”的原因它正是通过对程序栈的分析实现的。
http://www.zqtcl.cn/news/815139/

相关文章:

  • 邵阳网站建设制作电子商务网站开发软件
  • 怎样推广网站平台树莓派 wordpress mysql
  • 互联网公司网站建设wordpress发文章设置文字大小
  • 国科联创网站建设无锡网站建设有限公司
  • 网站开发官网源码石家庄怎样做网站
  • 做网站的开发工具北京公司网站制作电话
  • 试用体验网站3g微网站是什么
  • 响应式网站源代码什么是营销渠道
  • 深圳品牌做网站公司有哪些php的网站数据库如何上传
  • 关于医疗保障局门户网站建设青柠直播免费版
  • 微信网站制作免费平台微商城网站建设公司的价格
  • 古典风格网站模版广州网站建设加q.479185700
  • 建站工具推荐网站关键词在哪里添加
  • 国内简约网站汽车最好网站建设
  • 外文网站建设网站项目计划书模板范文
  • 免费婚庆网站模板深圳市龙华区繁华吗
  • 档案馆建设网站邢台又一地被划定高风险区域
  • 网站怎么赚钱的网站asp源码
  • 明星网站怎么设计新手怎么做网络销售
  • ps做网站72分辨率深鑫辉网站建设
  • 购物网站设计的目的html简单登录页面代码
  • 网站导航栏下载网页自助建站
  • 新手建立网站的步骤建设企业网站个人网银
  • 俄罗斯女孩制作论文网站wordpress和hexo
  • 南宁市网站设计wordpress主题安装教程
  • 网站取消备案怎样做国外电子商务网站
  • 学校建设网站费用申请青岛平台公司
  • 平面设计师个人网站怎样登录韵网网站
  • 怎么用eclipse做网站开发推广平台取名字
  • 深圳建网站服务商广东佛山建网站