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”标识结束。

  其格式为“?functionname@@YG*****@Z”或“?functionname@@YG*XZ”,例如

  int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z”

  void Test2() -----“?Test2@@YGXXZ”

  __cdecl调用约定:

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

  __fastcall调用约定:

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

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

 

 

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

转载于:https://www.cnblogs.com/misspy/p/3869237.html

STM32F103汇编与C交互原理:从反汇编看函数调用、栈和启动流程 嵌入式系统中,C语言的抽象执行背后是严格的硬件行为约束。理解程序如何在ARM Cortex-M3架构上启动、函数如何依据AAPCS调用约定传递参数、栈如何保存上下文、外设寄存器访问如何映射为读-改-写指令,是构建可靠裸机系统的基础。这些机制直接决定HardFault定位、实时性优化与内存安全等关键工程能力。本文以STM32F103点灯工程为载体,通过分析Keil生成的反汇编文件(.lst),揭示C代码到机器码的逐层映射关系,涵盖复位向量初始化、寄存器级外设操作、volatile对栈行为的影响以及链接脚本对内 阅读详情

相关推荐

从零开始:在龙芯3A5000上动手写你的第一个LoongArch汇编程序

本文详细介绍了如何在龙芯3A5000平台上编写、编译和运行第一个LoongArch汇编程序。从环境准备、工具链配置到Hello World程序编写,再到编译链接和调试,逐步引导开发者掌握LoongArch指令集的基础知识,为深入理解国产CPU架构提供实践指导。

weixin_30448603的博客 451

几种常用的函数调用约定

以下转自百度百科http://baike.baidu.com/view/1280676.htm         __cdecl 是C Declaration的缩写(declaration,声明),表示C语言默认的函数调用方法:所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈。被调用函数不会要求调用者传递多少参数,调用者传递过多或者过少的参数,甚至完全不同的参数都不会产生编译阶段的

zhizunwudi的专栏 1170

有关调用约定(调用惯例)

调用约定(有些书上也叫调用惯例)指的是,函数的调用方和被调用方对于函数如何调用所达成的一个约定。网上关于各种调用约定的说明已经比较多了,我这里主要说一下调用约定的由来。 参考资料: 《程序员的自我修养》第十章前两节。 调用约定的由来 我们知道,一个应用程序的内存空间中,有栈这个结构来维护函数调用的上下文,在进行一个函数的调用之前,通常都要先push进其参数(这里...

aguchu2119的博客 159

sql中常用关键字和函数的总结

sql中常用关键字和函数的总结 关键字 distinct:去掉重复的关键字 select distinct 列名 from 表名 -- order by :排序 -- desc 降序(默认降序) -- asc 升序 select 列名 from 表名 order by 列名1 desc,列名2 asc top :获取最上边几条的数据 --sql: select top 5 列名 from 表名...

weixin_44823472的博客 856

内存操作函数

内存操作函数 1、memset() 主要用于清0 /*#include <string.h> void *memset(void *s, int c, size_t n); 功能:将s的内存区域的前n个字节以参数c填入 参数: s:需要操作内存s的首地址 c:填充的字符,c虽然参数为int,但必须是unsigned char , 范围为0~255 n:指定需要设置的大...

CCai_x的博客 424

三种函数调用约定

转自:https://www.cnblogs.com/xuanyuan/p/3917615.html   小议三种函数调用约定 __cdecl、__stdcall、__fastcall是C/C++里中经常见到的三种函数调用方式。其中__cdecl是C/C++默认的调用方式,__stdcall是windows API函数的调用方式,只不过我们在头文件里查看这些API的声明的时候是用了WINAP...

HappyMrSpring 3078

第八节_函数调用与返回

学完本节,希望你能够:理解 ARM32 和 ARM64 下函数调用与返回是咋回事;掌握参数和返回值怎么传(寄存器、栈);会用 BL、BX、RET 在汇编里调用和返回;你只要记住这几条就行:ARM32 前 4 个参数用 R0~R3,返回值在 R0;ARM64 前 8 个参数用 X0~X7,返回值在 X0;(第九节),帮你搞清 ARM32/ARM64、x86、big.LITTLE 与 APK 里 so 的对应关系。调用前用 PUSH/STP 保存要保护的寄存器,返回前用 POP/LDP 恢复。

长沙火山 687

函数调用约定(转载)

在C语言中,假设我们有这样的一个函数:    int function(int a,int b)    调用时只要用result = function(1,2)这样的方式就可以使用这个函数。但是,当高级语言被编译成计算机可以识别的机器码时,有一个问题就凸现出来:在CPU中,计算机没有办法知道一个函数调用需要多少个、什么样的参数,也没有硬件可以保存这些参数。也就是说,计算机不知道怎么给这个函数传递参

bodybo的专栏 880

【无标题】

硬件只会自动保存上面的8个寄存器,,剩下的R4~R11,,共8个通用寄存器,,需要由中断服务函数的汇编代码,,或编译器自动生成指令,手动压栈保存。ARM cortex-M内核,,stm32常用的M3,M4,不带浮点单元FPU的完整CPU运行上下文(寄存器现场)大小,,由两部分栈空间组成。硬件自动保存栈帧 32字节,,, 当中断异常触发时,cortex-M 内核会 由硬件自动把8个关键寄存器压入栈中,,完全不需要软件干预。最坏情况,,所有寄存器都被使用,,全部入栈,,也就是64字节。中断现场的64字节,,,

m0_37710814的博客 174

从攻击者视角看缓冲区溢出:用南大Buflab实验拆解ROP、栈随机化等现代防御绕过技术

本文从攻击者视角深入解析缓冲区溢出技术,通过南京大学Buflab实验拆解ROP、栈随机化等现代防御绕过技术。文章详细介绍了栈帧操控、Shellcode注入、NOP雪橇和ROP攻击等关键技术,帮助读者理解计算机系统安全中的攻防博弈。

weixin_30772105的博客 390

PC微信逆向工程实战:解析登录二维码刷新机制

本文通过逆向工程实战,详细解析了PC微信登录二维码的刷新机制。从定位关键字符串入手,结合静态分析与动态调试,追踪到核心刷新函数调用链,并探讨了其背后的定时器与网络请求逻辑。文章旨在为客户端安全研究与软件设计学习提供技术参考,强调所有分析仅供学习交流,请严格遵守法律法规。

beta5的博客 447

c语言函数调用约定,函数调用约定

函数调用约定常见的函数调用约定[5]:cdecl,stdcall,fastcall,thiscall,naked callMFC调用约定(VS6:Project Settings->C/C++ Calling convention:)1. __cdecl(C调用约定.The C default calling convention)C/C++ 缺省调用方式a.压栈顺序:函数参数从右到左.b...

weixin_39630410的博客 601

剖析VC++函数调用约定

Visual C/C++ 的编译器提供了几种函数调用约定,了解这些函数调用约定的含义及它们之间的区别可以帮助我们更好地调试程序。在这篇文章里,我就和大家共同探讨一些关于函数调用约定的内容。      Visual C/C++ 的编译器支持如下的函数调用约定:   关键字 清理堆栈 参数入栈顺序 函数名称修饰(C) __cdecl 调用函数 右 à 左 _函数名 __s

★卢培培★ ── 欢迎光临卢培培(goodname008)的BLOG 1万+

函数调用约定与名字修饰约定

在Windows下,由于很多语言支持动态链接库技术,因此动态链接库是一种很好的混合编程方法。语言对函数的约定有两种:函数调用约定和名字修饰约定。不同语言默认的调用调用约定和函数的命名方式是不同的,要想不同的语言开发的动态链接库能够相互调用,那么开发动态链接库的语言和调用链接库的语言的函数约定必须相同,同时在编译时函数的修饰名也必须一样。

一只菜鸟的专栏 1790

函数调用约定

函数调用约定 函数调用约定的概念 函数调用约定,是指当一个函数被调用时,函数的参数会被传递给被调用的函数和返回值会被返回给调用函数。函数的调用约定就是描述参数是怎么传递和由谁平衡堆栈的,当然还有返回值。 函数调用约定规定了执行过程中函数的调用者和被调用者之间如何传递参数以及如何恢复栈平衡 在参数传递过程中,有二个问题必须得到明确说明:(1)当参数多于1个时,按照什么顺序把参数入栈(2)函数调用后 ,由谁把栈恢复原貌. 函数调用约定 _cdecl cdecl调用约定又称为C调用约定,是C语言缺省的调用约

weixin_43831728的博客 541

c语言之函数调用约定

在C和C++编程中,函数调用约定(Calling Convention)定义了函数参数如何传递、由谁负责清理栈(如果有的话)、以及返回值如何传递等规则。不同的平台和编译器可能采用不同的调用约定。不同的调用约定适用于不同的场景和需求。了解这些调用约定的特点和适用场景有助于编写更高效、更可移植的代码。在选择调用约定时,需要考虑目标平台、编译器、以及与其他代码或库的兼容性。

qq_45583706的博客 941

C++函数调用约定

函数调用方式FAR PASCAL,__cdecl,_stdcall,__pascal. 解释一: 1、通过栈传递,被调用的函数在返回前清理传送参数的内存栈,但不同的是函数名的修饰部分(关于函数名的修饰部分在后面将详细说明)。 _stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上

hacker00011000的专栏 820
上一篇: PAT_A1043#Is It a Binary Search Tree
下一篇: 【Spark深入学习 -13】Spark计算引擎剖析
weixin_34007886
博客等级 码龄11年 4160粉丝 845原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值