查看: 445|回复: 1

[C/C++] 函数调用方式

[复制链接]

0

技术

0

魅力

0

原创

初出茅庐

Rank: 2

积分
155
人气
3
分享
0
发表于 2022-12-30 09:24:43 | 显示全部楼层 |阅读模式
我发一个试试水。

函数调用方式:
_cdecl:C\C++默认的调用方式,调用方平衡栈,不定参数的函数可以使用这种方式
_stdcall:被调方平衡栈,不定参数的函数无法使用这种方式
_fastcall:寄存器方式传参,被调方平衡栈,不定参数的函数无法使用这种方式。
这个平衡栈,意思就是说,在子函数运行完成后,是由调用的一方释放栈存储空间,还是被调用的函数在返回之前平衡栈空间。
x86应用程序的函数调用有上面三种方式,但是x64应用程序只有1中调用方式,名为寄存器快速调用约定。前四个参数使用寄存器传递,多余的参数就放到栈空间中,参数的传递顺序从右到左,由函数调用方平衡占空间。前四个参数存放的寄存器是固定的表格如下。还有,任意大于8字节或者不是1字节,2字节,4字节,8字节的参数是通过引用传递的。
  
参数
  
整数类型
浮点类型
  
第1个参数
  
RCX
XMM0
  
第2个参数
  
RDX
XMM1
  
第3个参数
  
R8
XMM2
  
第4个参数
  
R9
XMM3
这里举个例子,如果是void func(float , float , int ,int)
那么前两个参数为XMM0,XMM1,后两个参数是R8,R9,而不是RCX,RDX。
       这里值得注意的是,虽然前四个参数使用寄存器传递,但是相对应的栈空间(32位)仍然会预留。因为寄存器数量有限,一旦寄存器数量不够用,就可以把寄存器的值保存到预留的栈空间中。预留的空间也是由函数调用方平衡的。

       在调用类成员函数的时候,还会遇到thiscall的默认调用约定,所有成员函数(非静态成员函数)都有一个隐藏参数,即自身类型的指针。调用成员函数过程中,默认利用ecx中保存的对象首地址,并以寄存器传参的方式将其传递到成员函数中。thiscall的栈平衡方式与__stdcall相同,都是被调用方负责平衡栈。在类中,如果定义的时候就选择其它调用方式,则this指针不再使用ecx传递,而是改用栈传递参数。
      另外,平衡栈的操作有时会随着优化选项的不同而不同,有时是连续调用多个函数后,一次性平衡栈。部分调用方式也不是固定的,不同的编译器也会存在一定区别,具体情况还是根据反汇编调试时看到的为准。

评分

参与人数 1经验 +11 人气 +3 收起 理由
YFSafe + 11 + 3

查看全部评分

1

技术

14

魅力

1

原创

退休版主

Rank: 8Rank: 8

积分
8180
人气
416
分享
59

论坛元老活跃会员灌水之王荣誉管理

发表于 2022-12-30 10:05:19 | 显示全部楼层
这个我在尝试在C++,包括C#里面调用Rust的Dll的时候研究过一点。这方面确实需要注意。首先平台要统一,因为C++和C#编译出来很多时候都是x86优先,然而Rust是默认x64的。其次调用方式要进行配置,看起来_cdecl都可以的样子。还有一个最让我头疼的FFI和Rust的String、str与C++中const char*和string,包括C#中的string的传递和转换,指针转来转去,不排除调用方式还有不兼容,最后只能实现传递拉丁字母和数字,我怀疑是编码有问题,在Dll中尝试传递String似乎一直都是一个难题。。(可能有点偏了,C++小白((
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表