三种函数调用约定

__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

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

原文地址:https://www.cnblogs.com/-qing-/p/10674223.html

时间: 2024-08-05 18:47:23

三种函数调用约定的相关文章

【系统篇】小议三种函数调用约定

小议三种函数调用约定 __cdecl.__stdcall.__fastcall是C/C++里中经常见到的三种函数调用方式.其中__cdecl是C/C++默认的调用方式,__stdcall是windows API函数的调用方式,只不过我们在头文件里查看这些API的声明的时候是用了WINAPI的宏进行代替了,而这个宏其实就是__stdcall了. 三种调用方式的区别相信大家应该有些了解,这篇文章主要从实例和汇编的角度阐述这些区别的表现形态,使其对它们的区别认识从理论向实际过渡. __cdecl: C

c++函数调用约定学习(二)

*********************************************************** 首先,比较C++ 中的三种函数调用方式. 测试代码: int _stdcall Add1(int x1, int x2, int x3) { return x1 + x2 + x3; } int __cdecl Add2(int x1, int x2, int x3) { return x1 + x2 + x3; } int __fastcall Add3(int x1, in

3 种关键函数调用约定

高级语言翻译成机器码后,计算机没有办法知道函数调用的参数个数.类型,也没有硬件可以保护这些参数. 另外,在C++中,因为重载的原因,所以对函数的命名方式和普通C语言并不一致,该方式称为名字改编. 函数调用者与函数之间,尤其是跨语言调用接口时,需要一个协议约定来传递参数——栈. 关键流程: 调用时,调用者依次把参数压栈,然后调用函数, 被调用函数,在堆栈中取得数据,并进行计算. 函数计算结束以后,或者调用者.或者函数本身修改堆栈,使堆栈恢复原装. 常见的函数调用约定: stdcall cdecl

[转载] 详述三种现代JVM语言--Groovy,Scala和Clojure

转载自http://www.tuicool.com/articles/jYzuAv和http://www.importnew.com/1537.html 在我与Martin Fowler曾经合作呈现的一次主题演讲中,他作出了一个有洞察性的观点: Java的遗产将是平台,而不是程序设计语言. Java技术的原始工程师们作出了一个明智的决定,就是将编程语言与运行时环境分开,最终这使得超过200种语言能够运行在Java平台上.这种架构对于该平台的长期活力是至关重要的,因为计算机程序设计语言的寿命一般都

64位只有一种调用约定stdcall

procedure TForm2.Button1Click(Sender: TObject); function EnumWindowsProc(Ahwnd: hwnd; AlParam: lParam): Boolean; stdcall; begin ShowMessage('hwnd:' + IntToStr(Ahwnd)); ShowMessage('lParam' + IntToStr(AlParam)); Result := True; end; begin EnumChildWin

c++函数调用约定学习(一)

函数调用约定 常见的函数调用约定[5]:cdecl,stdcall,fastcall,thiscall,naked call MFC调用约定(VS6:Project Settings->C/C++ <Category:Code Generation> Calling convention:) 1, __cdecl(C调用约定.The C default calling convention)C/C++ 缺省调用方式 1)压栈顺序:函数参数从右到左 2)参数栈维护:由调用函数把参数弹出栈,

详述三种现代JVM语言--Groovy,Scala和Clojure

Java的遗产将是平台,而不是程序设计语言. Java技术的原始工程师们作出了一个明智的决定,就是将编程语言与运行时环境分开,最终这使得超过200种语言能够运行在Java平台上.这种架构对于该平台的长期活力是至关重要的,因为计算机程序设计语言的寿命一般都是比较短.从2008年开始,由Oracle主办的一年一度的JVM语言峰会为JVM上其它的语言实现与Java平台工程师进行开放式合作提供了机遇. 欢迎来到Java.next专栏系列,在本系列的文章中,我将讲述三种现代JVM语言--Groovy,Sc

javascript——四种函数调用形式

此文的目的是分析函数的四种调用形式,弄清楚函数中this的意义,明确构造函对象的过程,学会使用上下文调用函数. 在JavaScript中,函数是一等公民,函数在JavaScript中是一个数据类型,而非像C#或 其他描述性语言那样仅仅作为一个模块来使用. 函数有四种调用模式,分别是:函数调用形式.方法调用形式.构造器形式.以及apply形式. 这里所有的调用模式中,最主要的区别在于关键字 this 的意义. 下面分别介绍这个几种调用形式. 一.函数调用形式 函数调用形式是最常见的形式,也是最好理

System V三种IPC编程巧学巧记

转载请保留原文地址http://blog.csdn.net/guodongxiaren/article/details/43876207 概述 System V("系统五")系统上发明了三种IPC机制(消息队列.信号量和共享内存),通常称为System V IPC.又因为后来被收录到Unix的XSI标准之中故又称为XSI IPC.所以当你看到System V IPC 和 XSI IPC的时候实际上指的是同一种东西. C语言是一门面向过程的语言,与OO(面向对象)语言不同,它没有做到数据