zoukankan      html  css  js  c++  java
  • Delphi 调侃调用方式

    技术交流,DH讲解.

    本随笔,就自己一心得+笔记,而自己水平有限,所以本文也可能是水文.哈哈
    讲解前先来个表格:有条件的朋友可以看加密解密(第三版)一书,哈哈,我书还是买了一些的.

    方式 传值方向 传值位置 谁来平衡堆栈 备注
    _cdecl 从右到左 直接压栈 调用者 默认C++的
    _stdcall 从右到左 直接压栈 函数本身 Win32 API
    register 从左到右 adc寄存器然后压栈 函数本身 Delphi默认的
    pascal 从左到右 直接压栈 函数本身  
    safecall       同stdcall
    不同调用有什么区别呢?
    function TestStdcall(a,b:Integer):Integer ;stdcall;
    var
      c:Integer;
    begin
      c:=a+b;
      if c>2*a then
        Dec(c,b)
      else
        Dec(c,a);
      Result:=c;
    end;
    
    function TestCdecl(a,b:Integer):Integer ;cdecl;
    var
      c:Integer;
    begin
      c:=a+b;
      if c>2*a then
        Dec(c,b)
      else
        Dec(c,a);
      Result:=c;
    end;
    
    function TestRegister(a,b:Integer):Integer ;
    var
      c:Integer;
    begin
      c:=a+b;
      if c>2*a then
        Dec(C, B)
      Else
        Dec(C, A);
      Result:= C;
    End;
    {$R *.dfm}
    
    Procedure TForm4.FormCreate(Sender: TObject);
    Var
      C: Integer;
    Begin
      C:= TestStdcall(5, 6);
      ShowMessage(IntToStr(C));
      C:= TestCdecl(5, 6);
      ShowMessage(IntToStr(C));
      C:= TestRegister(5, 6);
      ShowMessage(IntToStr(C));
    End;

    代码都一样,我们来看看执行时候有什么不一样的地方呢?
    第一个TestStdcall:
    image
    我们看见先压6再压5,也就是从右到左压栈.函数内部:
    image
    先将栈里面的参数取出来放到ecx和edx中,最后清除栈Ret 8;
    stdcall很明显了.

    接下来看cdecl调用方式吧.
    image
    也是先压6再压5,但是我们看到最后面add esp,8,这个就是平衡栈了,因为我们压入了2个Integer,8个字节.
    函数内部:
    image
    还是先从栈里面去参数到寄存器,最后直接ret咯.

    接下来是Delphi默认的方式:
    image
    将5传入eax,6传入edx,它这里虽然现传的6,但是我们要注意寄存器的顺序应该是 eax,edx,ecx,如果还有多余的参数再压栈.
    函数内部:
    image
    晕这个例子没有选好,因为参数没有用到栈,所以不存在栈平衡,我改一下...
    image
    从压栈的顺序我们再一次看出来了,是从左到右的,先压的8.
    image
    释放参数压栈用的空间,ret 8;

    最后改一下,来看看Pascal调用方式,这个是Delphi1.0时候用的:
    image
    从左到右压栈的.
    image
    细心的朋友会发现这次没有讲栈里面东西取到寄存器中去,主要因为这次运算太简单了.哈哈,最后自己清除压的栈.

    为什么要了解这些?
    答案肯定是为了程序能够正常的工作了.这个问题...

    如果声明和调用的方式不一样会怎么样?
    1 走错路.如果只是从左到右和从右到左弄错了,程序能运行,只是参数a被当成参数b来用,也就是结果可能不对.
    2 走上不归路.因为我们知道堆栈里面保存了函数的返回地址这些,但是如果我们调用方式不一样就可能造成堆栈被破坏了,程序无法正常返回,就会报错了.

    ok,个人理解.

  • 相关阅读:
    switch语句相关
    大根堆的创建过程
    总结Java中线程的状态及多线程的实现方式
    用直接路径(direct-path)insert提升性能的两种方法
    Oracle的日志记录模式
    针对WebLogic Server 12.1.3版本打补丁
    Oracle Service Bus中的线程
    简化调用Web Service
    网络知识收集
    WebLogic MBean Monitor
  • 原文地址:https://www.cnblogs.com/huangjacky/p/1655758.html
Copyright © 2011-2022 走看看