为什么一个方法最好不超过4个参数,这样设计有什么好处?
摘要:简介 在很多年前的一次Code Review中,有大佬指出,方法的参数太多了,最好不要超过四个,对于当时还是萌新的我,虽然不知道什么原因,但听人劝,吃饱饭,这个习惯也就传递下来了,直到参加工作很多年后,才明白这其中的缘由。 调用协定 在计算
简介
在很多年前的一次Code Review中,有大佬指出,方法的参数太多了,最好不要超过四个,对于当时还是萌新的我,虽然不知道什么原因,但听人劝,吃饱饭,这个习惯也就传递下来了,直到参加工作很多年后,才明白这其中的缘由。
调用协定
在计算机编程中,调用协定(Calling Convention)是一套关于方法/函数被调用时参数传递方式,栈由谁清理和寄存器如何使用的规范。
参数传递方式
寄存器传递:将参数存入CPU寄存器,速度最快。
栈传递:将参数压入调用栈,再依次从栈中取出,速度最慢
混合传递:前N个参数用寄存器,剩余参数用栈,速度适中
栈由谁清理
Caller清理:调用函数后由调用方负责恢复栈指针(如C/C++的__cdecl)。
Callee清理:被调用函数返回前自行清理栈(如x64的默认协定)。
寄存器如何使用
易变寄存器(Volatile Registers):函数调用时可能被修改的寄存器(如x64的RAX、RCX、RDX),调用方需自行保存这些寄存器的值。
非易变寄存器(Non-Volatile Registers):函数必须保存并恢复的寄存器(如x64的RBX、RBP、R12-R15)。
x86架构混乱的调用协定
x86架构发展较早,因此调用协定野蛮生长,有多种调用协定
协定名称
参数传递方式
栈清理
适用场景
__cdecl
通过栈传递(右→左)
调用者清理栈
C/C++默认,支持可变参数
__stdcall
通过栈传递(右→左)
被调用者清理栈
Windows API(如Win32)
__fastcall
前两个参数通过寄存器,剩余通过栈(右→左)
被调用者清理栈
高性能场景
__thiscall
this指针通过寄存器, 剩余通过栈(右→左)
被调用者清理栈
C++类成员函数
眼见为实
可以看到,cdecl,stdcall是通过压栈的方式将参数压入栈中,而fastcall直接赋值给寄存器,并无压栈操作
点击查看代码
#include <iostream>
int __cdecl cdecl_add(int a, int b) {
return a + b;
}
int __stdcall stdcall_add(int a, int b) {
return a + b;
}
int __fastcall fastcall_add(int a, int b) {
return a + b;
}
class Calculator {
public:
int __thiscall thiscall_add(int b) {
return this->a + b;
}
int a;
};
int main()
{
int a = 10, b = 5;
int cdecl_add_value = cdecl_add(a, b);
int stdcall_add_value = stdcall_add(a, b);
int fastcall_add_value = fastcall_add(a, b);
Calculator calc;
calc.a = 10;
int thiscall_add_value = calc.thiscall_add(5);
}
x64的大一统
而在x64架构下,为了解决割裂的调用协定,windows与linux实现了统一。
