3 种关键函数调用约定

高级语言翻译成机器码后,计算机没有办法知道函数调用的参数个数、类型,也没有硬件可以保护这些参数。

另外,在C++中,因为重载的原因,所以对函数的命名方式和普通C语言并不一致,该方式称为名字改编。

函数调用者与函数之间,尤其是跨语言调用接口时,需要一个协议约定来传递参数——栈。

关键流程:

调用时,调用者依次把参数压栈,然后调用函数,

被调用函数,在堆栈中取得数据,并进行计算。

函数计算结束以后,或者调用者、或者函数本身修改堆栈,使堆栈恢复原装。

常见的函数调用约定:

  • stdcall
  • cdecl
  • fastcall
  • thiscall
  • naked call

关键字__cdecl、__stdcall和__fastcall可以直接加在要输出的函数前,

也可以在编译环境的Setting...-> C/C++->Code Generation项选择。

它们对应的命令行参数分别为/Gd、/Gz和/Gr。缺省状态为/Gd,即__cdecl。

当加在输出函数前的关键字与编译 环境中的选择不同时,直接加在输出函数前的关键字有效。

其中有 2 个关键问题:

  1. 参数入栈顺序
  2. 谁负责清空栈

入栈顺序上,一般都是按照从右往左入栈。

关键分歧在于谁负责清空,分别对应 2 个典型的约定:stdcall, cdecl

stdcall 约定——pascal调用约定。WINAPI, CALLBACK

  • 参数从右向左压入堆栈。
  • 函数自身修改堆栈。
  • 函数名自动加前导的下划线"_"、后缀"@参数的尺寸"。

cdecl 约定——C调用约定,C语言默认

  • 参数从右向左压入堆栈。
  • 函数本身不清理堆栈,调用者负责清理堆栈。
  • 函数名自动加前导的下划线"_"、没有后缀。

函数本身不清理堆栈,调用者负责清理堆栈。由于这种变化,C调用约定允许函数的参数的个数是不固定的。

由于参数按照从右向左顺序压栈,因此最开始的参数在最接近栈顶的位置,因此

当采用不定个数参数时,第一个参数在栈中的位置肯定能知道,只要不定的参数个数能够根据第一个或者后续的明确的参数确定下来,就可以使用不定参数,

例如 sprintf 函数,

int sprintf(char* buffer,const char* format,...)

由于所有的不定参数都可以通过format确定,因此使用不定个数的参数是没有问题的。

thiscall

唯一不能明确指明的函数修饰,

因为thiscall不是关键字。它是C++类成员函数缺省的调用约定。

由于成员函数调用还有一个this指针,因此必须特殊处理,

  • 参数从右向左入栈
  • 如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压栈后被压入堆栈。
  • 对参数个数不定的,调用者清理堆栈,否则函数自己清理堆栈

WINAPI
可以被翻译成适当的调用约定以供函数使用。该宏定义于windef.h之中。下面是在windef.h中的部分内容:

#define CDECL _cdecl
#define WINAPI CDECL
#define CALLBACK __stdcall
#define WINAPI __stdcall
#define APIENTRY WINAPI

C++编译时函数名修饰约定规则:

  __stdcall调用约定:

  1、以“?”标识函数名的开始,后跟函数名;

  2、函数名后面以“@@YG”标识参数表的开始,后跟参数表;

  3、参数表以代号表示:

  X--void ,

  D--char,

  E--unsigned char,

  F--short,

  H--int,

  I--unsigned int,

  J--long,

  K--unsigned long,

  M--float,

  N--double,

  _N--bool,

  ....

  PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代

  表一次重复;

  4、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前

  ;

  5、参数表后以“@Z”标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。

  其格式为“[email protected]@YG*****@Z”或“[email protected]@YG*XZ”,例如

  int Test1(char *var1,unsigned long)-----“[email protected]@[email protected]”

  void Test2() -----“[email protected]@YGXXZ”

  __cdecl调用约定:

  规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YA”。

  __fastcall调用约定:

  规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YI”。

  VC++对函数的省缺声明是"__cedcl",将只能被C/C++调用.

如果定义的约定和使用的约定不一致,则将导致堆栈被破坏。

3 种关键函数调用约定

时间: 2024-11-10 07:08:24

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

__stdcall函数调用约定

__stdcall 被这个关键字修饰的函数,其参数都是从右向左通过堆栈传递的(__fastcall 的前面部分由ecx,edx传), 函数调用在返回前要由被调用者清理堆栈. 这个关键字主要见于Microsoft Visual C.C++.GNU的C.C++是另外一种修饰方式:__attribute__((stdcall)) 1函数调用约定 __stdcall是函数调用约定的一种,函数调用约定主要约束了两件事: 1.参数传递顺序 2.调用堆栈由谁(调用函数或被调用函数)清理 常见的函数调用约定:s

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

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

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)参数栈维护:由调用函数把参数弹出栈,

C语言函数调用约定

在C语言中,假设我们有这样的一个函数: int function(int a,int b) 调用时只要用result = function(1,2)这样的方式就可以使用这个函数.但是,当高级语言被编译成计算机可以识别的机器码时,有一个问题就凸现出来:在CPU中,计算机没有办法知道一个函数调用需要多少个.什么样的参数,也没有硬件可以保存这些参数.也就是说,计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用者和函数本身来协调.为此,计算机提供了一种被称为栈的数据结构来支持参数传递. 栈

汇编 ? cdecl 函数调用约定,stdcall 函数调用约定

知识点: ? cdecl 函数调用约定 ? stdcall 函数调用约定 ? CALL堆栈平衡 配置属性--> c/c++ -->高级-->调用约定 一.cdecl调用约定 VC++默认约定__cdecl 1.源代码 int __cdecl add1(int a,int b) { return a+b; } 2.生成汇编代码 00401000 /$ 55 PUSH EBP 00401001 |. 8BEC MOV EBP,ESP 00401003 |. 8B45 08 MOV EAX,D

C/C++函数调用约定

C/C++函数调用约定 函数声明部分的extern "C"表示连接规范(Linkage Specification)采用C,而不是C++.如果不写的 话.默认采用C++,当然也可以写成extern "C++". 1.__cdecl: C和C++默认的函数调用约定,参数从右到左顺序压入堆栈,由函数负责清理堆栈,把参数弹出. 也正是因为用来传送参数的堆栈是由调用函数维护的,所以实现可变参数的函数只能使用这种函数调用约定.因为每一个调用它的函数都要包含清理堆栈的代码,所以

函数调用约定和堆栈

函数调用约定和堆栈 1 什么是堆栈 编译器一般使用堆栈实现函数调用.堆栈是存储器的一个区域,嵌入式环境有时需要程序员自己定义一个数组作为堆栈.Windows为每个线程自动维护一个堆栈,堆栈的大小可以设置.编译器使用堆栈来堆放每个函数的参数.局部变量等信息. 函数调用经常是嵌套的,在同一时刻,堆栈中会有多个函数的信息,每个函数占用一个连续的区域.一个函数占用的区域被称作帧(frame). 编译器从高地址开始使用堆栈. 假设我们定义一个数组a[1024]作为堆栈空间,一开始栈顶指针指向a[1023]

C++语言学习(十二)——C++语言常见函数调用约定

C++语言学习(十二)--C++语言常见函数调用约定 一.C++语言函数调用约定简介 C /C++开发中,程序编译没有问题,但链接的时候报告函数不存在,或程序编译和链接都没有错误,但只要调用库中的函数就会出现堆栈异常等现象.上述现象出现在C和C++的代码混合使用的情况下或在C++程序中使用第三方库(非C++语言开发)的情况下,原因是函数调用约定(Calling Convention)和函数名修饰(Decorated Name)规则导致的.函数调用约定决定函数参数入栈的顺序,以及由调用者函数还是被

三种函数调用约定

__cdecl.__stdcall.__fastcall是C/C++里中经常见到的三种函数调用方式.其中__cdecl是C/C++默认的调用方式,__stdcall是windows API函数的调用方式,只不过我们在头文件里查看这些API的声明的时候是用了WINAPI的宏进行代替了,而这个宏其实就是__stdcall了. __cdecl: C/C++默认方式,参数从右向左入栈,主调函数负责栈平衡. __stdcall:            windows API默认方式,参数从右向左入栈,被调