zoukankan      html  css  js  c++  java
  • stdcall与cdecl的区别

    1 区别

    VC++C/C++函数有两种基本的调用约定:__stdcall__cdecl。它们有什么区别呢?请参考下表:

       

    __stdcall 

    __cdecl 

    函数代码

    C

    int __stdcall addS(int a,int b)

    {

    return a + b;

    } 

    int __cdecl addC(int a,int b)

    {

    return a + b;

    }

    ASM32

    push    ebp

    mov    ebp,esp

    sub    esp,40h

    push    ebx

    push    esi

    push    edi

    lea    edi,[ebp-40h]

    mov    ecx,10h

    mov    eax,0CCCCCCCCh

    rep    stos dword ptr [edi]

    mov    eax,dword ptr [ebp+8]

    add    eax,dword ptr [ebp+0Ch]

    pop    edi

    pop    esi

    pop    ebx

    mov    esp,ebp

    pop    ebp

    ret    8

    push    ebp

    mov    ebp,esp

    sub    esp,40h

    push    ebx

    push    esi

    push    edi

    lea    edi,[ebp-40h]

    mov    ecx,10h

    mov    eax,0CCCCCCCCh

    rep    stos dword ptr [edi]

    mov    eax,dword ptr [ebp+8]

    add    eax,dword ptr [ebp+0Ch]

    pop    edi

    pop    esi

    pop    ebx

    mov    esp,ebp

    pop    ebp

    ret

    函数调用

    C

    addS(1,2); 

    addC(1,2);

    ASM32

    push    2

    push    1

    call    @ILT+85(addS)

    push    2

    push    1

    call    @ILT+50(addC)

    add    esp,8

    说明:

    1、函数 addS、addC 的调用约定分别是__stdcall、__cdecl,它们32位汇编代码的不同之处仅仅在于ret 8和ret;

    2addS(1,2)addC(1,2)32位汇编代码不同之处在于后者多了一个add esp,8

    3、寄存器esp保存着程序堆栈的栈顶地址

    调用addS(1,2)的过程是这样的:push 2push 1用来把参数压入堆栈,压入了两个4字节的int,所以esp的数值会减去8。然后call语句调用addS的汇编代码。形参a的数值是dword ptr [ebp+8],即push 1压入的1;形参b的数值是dword ptr [ebp+0Ch],即push 2压入的2ebp其实是esp的初始值(mov ebp,esp),所以参数ab是取自堆栈的。addS返回时调用的是ret 8,亦即返回时会将寄存器esp的数值加上8,完成堆栈的清除工作。

    调用addC(1,2)的过程与addS(1,2)大致相同,不同之处在于ret不会修改寄存器esp,不会完成堆栈的清除工作。在执行add esp,8时,才会将esp寄存器的数值加上8,完成堆栈的清除工作。

    简单的说,就是:__stdcall的函数在返回时会自动清除堆栈中的参数;__cdecl的函数在返回时不会自动清除堆栈中的参数,清除工作由调用者完成。

    2 函数指针

    再看看下面的例子:

    函数调用

    C 

    int(__stdcall*pfnS)(int,int)=&addS;

    (*pfnS)(1,2);

    int(__cdecl*pfnC)(int,int)=&addC;

    (*pfnC)(1,2);

    ASM32

    mov    dword ptr [ebp-14h],offset @ILT+85(addS) (0040105a)

    mov    esi,esp

    push    2

    push    1

    call    dword ptr [ebp-14h]

     

    cmp    esi,esp

    call    _chkesp (00401f1e)

    mov    dword ptr [ebp-18h],offset @ILT+50(addC) (00401037)

    mov    esi,esp

    push    2

    push    1

    call    dword ptr [ebp-18h]

    add    esp,8

    cmp    esi,esp

    call    _chkesp (00401f1e)

    调用(*pfnS)(1,2)比调用addS(1,2)多了如下代码:

    mov    esi,esp            //保存寄存器esp的数值至寄存器esi

    ... ... ...

    cmp    esi,esp            //查看寄存器esp的数值是否变化了

    call    _chkesp (00401f1e)     //寄存器esp的数值变化了,提示出错

    也就是说:通过函数指针调用函数,会检查寄存器esp的数值是否被正常恢复。

    如果强制 pfnS 指向 addC,然后执行(*pfnS)(1,2);,会发生什么?请参考如下代码:

    int(__stdcall*pfnS)(int,int)=(int(__stdcall*)(int,int))&addC;

    (*pfnS)(1,2);

    运行时会产生如下错误提示,说明寄存器 ESP 产生了错误:

  • 相关阅读:
    python目录
    面向对象
    模块(二)
    python函数(四)
    助教工作总结
    第五次个人作业:个人总结
    Typroa编写的图片上传博客园
    msfconsole利用ms17-010和ms12-020攻击
    第四次个人作业——案例分析
    助教周报(第二轮)
  • 原文地址:https://www.cnblogs.com/hanford/p/6177872.html
Copyright © 2011-2022 走看看