【汇编 C】C语言常用的三种函数调用约定:__cdecl、__stdcall、__fastcall

17 篇文章 1 订阅

目录

前言

C语言常用的调用约定

一、__cdecl调用约定

二、__stdcall调用约定

三、__fastcall调用约定

总结

结语

封面


前言

        本篇文章使用的工具是vs2010,内容可能涉及到汇编的知识,建议有一些汇编基础的再来看,不过没有汇编基础也没有关系,了解一下这三种调用约定即可。

C语言常用的调用约定

        以下就是C语言常用的三种调用约定:

调用约定参数压栈顺序平衡堆栈
__cdecl从右往左依次入栈调用者清理堆栈
__stdcall从右往左依次入栈自身清理堆栈
__fastcall

ECX/EDX传递前两个参数

剩下的从右往左依次入栈

自身清理堆栈

        下面会举例为大家讲解三种调用约定的区别。

一、__cdecl调用约定

        这是C语言默认的调用约定,使用的平栈方式为外平栈

        示例代码:

        以下代码不使用任何调用约定,让我们来看看函数默认的调用约定是什么。

#include <stdio.h>

int method(int x,int y)
{
    return x+y;
}

int main()
{
    __asm mov eax,eax;    // 此处设置断点

    method(1,2);

    return 0;
}

        编译、调试、ALT+8调出反汇编如下:

        根据上面这张图的描述,默认的约定很符合__cdecl约定。 

        使用cdecl约定,如下:

 

        vs2010:Ctrl+Alt+F7重新生成、F5调试、ALT+8查看反汇编:

        一模一样,可以看出__cdecl就是C语言默认的调用约定 。

二、__stdcall调用约定

        和__cdecl一样都是从右往左入栈参数,不过该调用约定使用的平栈方式是内平栈

        示例代码:

         Ctrl+Alt+F7重新生成、F5调试、ALT+8查看反汇编:

        可以看到,这里已经看不到堆栈的处理了。

        F11不断执行,直到进入call指令调用的method函数中:

 

        平栈操作跑到函数内部了,__cdecl约定是调用者(main)函数进行平栈,而__stdcall约定是函数内部自身进行平栈。 

三、__fastcall调用约定

        这是一个比较特殊的调用约定,当函数参数为两个或者以下时,该约定的效率远远大于上面两种,当然随着参数越来越多,该约定与上面两种约定的差距逐渐缩小。

        证明如下:

        首先,我们使用__fastcall调用约定并传入两个参数。

        重新生成、调试、汇编:

        F11进入函数内部查看:

        可以看出函数内部和外部都没有清理堆栈的操作。

        这也就是__fastcall效率高的原因。

        因为寄存器就是属于cpu的,然后堆栈是内存,使用cpu进行操作的效率肯定会大于使用内存,所以我们使用寄存器的效率肯定比push传参效率高很多啊。

        那么为什么没有平栈操作呢?

        因为我们没有使用堆栈啊,我们只是用了寄存器,并没有使用堆栈操作。

        但是当我们传入更多的参数的时候就需要用到堆栈了,因为__fastcall他只给我们提供了两个寄存器ECX/EDX可以用来传参。

        四个参数试试:

        重新生成、调试、汇编:

        F11进入函数内部查看:

        通过四个参数的传递,证明了:

        函数参数除了前两个参数使用寄存器、其他的依旧使用堆栈从右往左传参,并且是自身清理堆栈,不是调用者清理。

        思考为什么参数越来越多的时候,__fastcall与其他调用约定的差距越来越小呢?

        答:首先我们知道了使用寄存器(cpu)的效率远远大于使用堆栈(内存),然而__fastcall约定也只能使用两个寄存器,当函数参数只有两个时,__fastcall可以完全使用寄存器进行函数传参,所以这个时候他和__cdecl和__stdcall的差距最大。随着参数越来越多,__fastcall依旧只能使用两个寄存器,这样一来参数越多,__fastcall使用内存的占比就越大,所以性能差距也就越来越小。

总结

        以上的内容汇总如下:

调用约定参数压栈顺序平衡堆栈调用约定特点
__cdecl从右往左依次入栈调用者清理堆栈这是C语言默认的调用约定,使用的平栈方式为外平栈
__stdcall从右往左依次入栈自身清理堆栈和__cdecl一样都是从右往左入栈参数,不过该调用约定使用的平栈方式是内平栈
__fastcall

ECX/EDX传递前两个参数

剩下的从右往左依次入栈

自身清理堆栈这是一个比较特殊的调用约定,当函数参数为两个或者以下时,该约定的效率远远大于上面两种,当然随着参数越来越多,该约定与上面两种约定的差距逐渐缩小。

结语

        如果有听不懂的地方可以私信我,有错误的地方望指出。

封面

 

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值