前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2020-09-04:函数调用约定了解么?

2020-09-04:函数调用约定了解么?

原创
作者头像
福大大架构师每日一题
修改2020-09-07 10:18:20
5650
修改2020-09-07 10:18:20
举报

福哥答案2020-09-04:

初级回答:

stdcall和cdecl两者的参数传递顺序都是从右向左。

不同点是stdcall在被调用函数 (Callee) 返回前,由被调用函数 (Callee) 调整堆栈。cdecl在被调用函数 (Callee) 返回后,由调用方 (Caller) 调整堆栈,每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。

中级回答:

1.__stdcall

在被调用函数 (Callee) 返回前,由被调用函数 (Callee) 调整堆栈

参数从右向左压入堆栈。

函数名自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸。

2.__cdecl

在被调用函数 (Callee) 返回后,由调用方 (Caller) 调整堆栈。

函数实参在线程栈上按照从右至左的顺序依次压栈。

函数结果保存在寄存器EAX/AX/AL中

浮点型结果存放在寄存器ST0中

编译后的函数名前缀以一个下划线字符

调用者负责从线程栈中弹出实参(即清栈)

8比特或者16比特长的整形实参提升为32比特长。

受到函数调用影响的寄存器(volatile registers):EAX, ECX, EDX, ST0 - ST7, ES, GS

不受函数调用影响的寄存器: EBX, EBP, ESP, EDI, ESI, CS, DS

RET指令从函数被调用者返回到调用者(实质上是读取寄存器EBP所指的线程栈之处保存的函数返回地址并加载到IP寄存器)

3.__fastcall

__fastcall调用的主要特点就是快,因为它是通过寄存器来传送参数的。

实际上__fastcall用ECX和EDX传送前两个DWORD或更小的参数,剩下的参数仍自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈。

__fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,格式为@function@number,如double multi(double a, double b)的修饰名是@multi@16。

__fastcall和__stdcall很象,唯一差别就是头两个参数通过寄存器传送。注意通过寄存器传送的两个参数是从左向右的,即第1个参数进ECX,第2个进EDX,其他参数是从右向左的入栈,返回仍然通过EAX。

fastcall调用约定和stdcall类似,它意味着:

1) 函数的第一个和第二个DWORD参数(或者尺寸更小的)通过ecx和edx传递,其他参数通过从右向左的顺序压栈;

2) 被调用函数清理堆栈;

3) 函数名修改规则同stdcall。

Fast Calling Convention,快速调用约定。通过使用寄存器解决效率问题。特点:

函数参数部分通过寄存器传递,函数中最左的两个DWORD(寄存器大小是双字)或者更小的参数,通过寄存器传递。剩下的从右到左堆栈传递。 函数名改编:“@函数名@函数参数字节大小十进制”。 返回方式同__stdcall。

4.__thiscall

thiscall是唯一一个不能明确指明的函数修饰,因为thiscall不是关键字。它是C++类成员函数缺省的调用约定。由于成员函数调用还有一个this指针,因此必须特殊处理,thiscall意味着:

1) 参数从右向左入栈;

2) 如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压栈后被压入堆栈;

3) 对参数个数不定的,调用者清理堆栈,否则函数自己清理堆栈。

主要用于解决this指针问题,使用寄存器传递this指针。返回方式同__stdcall.

5.__nakedcall

这是一个很少见的调用约定,一般程序设计者建议不要使用。编译器不会给这种函数增加初始化和清理代码,更特殊的是,不能用return返回返回值,只能用插入汇编返回结果。

6.__pascal

基于Pascal语言的调用约定,参数从左至右入栈(与cdecl相反)。被调用者负责在返回前清理堆栈。 此调用约定常见在如下16-bit 平台的编译器:OS/2 1.x,微软Windows 3.x,以及Borland Delphi版本1.x。

7.__vectorcall

目的是用来优化浮点向量运算,intel处理器种有很多浮点向量寄存器,传统的调用约定(stdcall cdecl fastcall thiscall) 都是通过通用寄存器(ecx edx /rcx rdx r8 r9)以及堆栈进行参数传递,所以调用的时候,浮点参数需要从栈获取。

要求尽可能在寄存器中传递参数。函数名改编为”@@函数名@参数字节数十进制”。这是微软自己添加的标准。

8.syscall

与cdecl类似,参数被从右到左推入堆栈中。EAX, ECX和EDX不会保留值。参数列表的大小被放置在AL寄存器中(?)。 syscall是32位OS/2 API的标准。

9.optlink

参数也是从右到左被推入堆栈。从最左边开始的三个字符变元会被放置在EAX, EDX和ECX中,最多四个浮点变元会被传入ST(0)到ST(3)中----虽然这四个参数的空间也会在参数列表的栈上保留。函数的返回值在EAX或ST(0)中。保留的寄存器有EBP, EBX, ESI和EDI。 optlink在IBM VisualAge编译器中被使用。

10.__clrcall

__clrcall是C++ .Net里面的。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档