函数调用的三种约定,你都清楚吗

开发 后端
三种调用方式的区别相信大家应该有些了解,这篇文章主要从实例和汇编的角度阐述这些区别的表现形态,使其对它们的区别认识从理论向实际过渡。

 __cdecl、__stdcall、__fastcall是C/C++里中经常见到的三种函数调用方式。其中__cdecl是C/C++默认的调用方式,__stdcall是windows API函数的调用方式,只不过我们在头文件里查看这些API的声明的时候是用了WINAPI的宏进行代替了,而这个宏其实就是__stdcall了。

三种调用方式的区别相信大家应该有些了解,这篇文章主要从实例和汇编的角度阐述这些区别的表现形态,使其对它们的区别认识从理论向实际过渡。

我们知道,函数的调用过程是通过函数栈帧的不断变化实现的:

函数的调用,涉及参数传递,返回值传递,调用后返回,这都是通过栈的变化来实现的,对于三种调用约定而言:

__cdecl:

C/C++默认方式,参数从右向左入栈,主调函数负责栈平衡。

__stdcall:

windows API默认方式,参数从右向左入栈,被调函数负责栈平衡。

__fastcall:

快速调用方式。所谓快速,这种方式选择将参数优先从寄存器传入(ECX和EDX),剩下的参数再从右向左从栈传入。因为栈是位于内存的区域,而寄存器位于CPU内,故存取方式快于内存,故其名曰“__fastcall”。

下面从实例来认识一下这三种调用约定。先来看一个简单的不能再简单的程序了:

 

三个函数的内容都是一样的,不同的是使用了三种调用的方式。我们先来看看在main函数调用三个函数的时候的汇编代码: 

按照上面说的那样,__cdecl按照参数从右向左的方式进入栈区,注意Fun1()和Fun3()的区别,Fun1()在call Fun1()之后执行了add esp,8。这一操作正是我们前面所说的进行栈的平衡。调用函数之前连续进行了两次push操作将函数所需的实参5和2先后压入了栈区,调用完成后,我们需要恢复调用前的状态,则需调整栈顶指针esp的位置,这一工作由谁来完成就决定了两种函数调用方式__cdecl(主调函数完成)和__stdcall(被调函数完成)的区别。上图我们看到了__cdecl中由主调函数完成了,那么__stdcall呢,在被调函数Fun3()中,转向被调函数结尾处的代码,我们看到了这一句:

那么Fun1()结尾处又是如何呢?

看到了吧,这个ret指令后面跟没跟值就决定了函数返回是栈指针ESP需要增加的量。这样,不需要主调函数再调用add指令为ESP操作平衡栈区,节约了程序的开销,一条指令开销小,如果十万百万个这样的调用,这个开销就明显了。

说完了__cdecl和__stdcall,再来看看__fastcall,如前面图看到的调用时并未使用push指令向栈里传参数,而是使用了

  mov  edx, 5

  mov  ecx, 2

两条指令。这样直接将参数传入寄存器,被调函数在执行的时候直接从寄存器取值即可,省去了从栈里取出来给寄存器,再从寄存器取出来放入内存。

不过,说个题外话,ecx寄存器经常作为计数和C++里this指针的传递媒介。在这种情况下,情况又是怎样的呢,下次分析C++操作符 new 的时候再予以讨论。ecx做计数器时,需要将ecx中存储的实参先压入栈区,计数操作完成后再pop出来。如此一来,这个fastcall倒显得不那么fast了。

当然,上面所说的这些操作都是由编译器在背后为我们完成的,开发人员无需关心这些操作,对我们是透明的。不过,知其然更知其所以然方能立于不败之地! 

 

责任编辑:庞桂玉 来源: C语言与C++编程
相关推荐

2024-08-26 07:59:04

2019-05-08 10:50:37

交换机组网网络

2021-09-13 13:05:05

Redis数据库内存

2023-02-27 23:45:09

MySQL索引存储

2023-08-04 08:25:03

客户配置Spring

2023-09-22 08:01:10

2023-09-14 23:14:57

MySQL索引

2021-02-25 23:40:26

Redis集群架构

2023-02-02 14:24:08

物联网数据分析云平台

2022-01-10 07:57:25

Linux 插桩Linux 系统

2024-05-27 00:20:00

2011-01-18 15:35:59

jQueryJavaScriptweb

2024-08-13 08:25:16

C#外部程序方式

2021-10-10 12:29:27

机器人AI人工智能

2022-07-09 15:37:14

数字化转型企业数字化

2020-09-11 06:39:29

ThreadLocal线程

2010-02-03 15:46:15

C++函数传递

2023-03-07 10:32:34

Go语言结构体

2024-04-11 12:57:55

Python函数

2021-03-17 09:59:26

Python函数调用
点赞
收藏

51CTO技术栈公众号