写点什么

为什么说方法的参数最好不要超过 4 个?

作者:秃头小帅oi
  • 2025-07-01
    福建
  • 本文字数:1369 字

    阅读完需:约 4 分钟

简介

在很多年前的一次 Code Review 中,有大佬指出,方法的参数太多了,最好不要超过四个,对于当时还是萌新的我,虽然不知道什么原因,但听人劝,吃饱饭,这个习惯也就传递下来了,直到参加工作很多年后,才明白这其中的缘由。

调用协定

在计算机编程中,调用协定(Calling Convention)是一套关于方法/函数被调用时参数传递方式栈由谁清理寄存器如何使用的规范。

  1. 参数传递方式

  • 寄存器传递:将参数存入 CPU 寄存器,速度最快。

  • 栈传递:将参数压入调用栈,再依次从栈中取出,速度最慢

  • 混合传递:前 N 个参数用寄存器,剩余参数用栈,速度适中

  1. 栈由谁清理

  • Caller 清理:调用函数后由调用方负责恢复栈指针(如 C/C++的__cdecl)。

  • Callee 清理:被调用函数返回前自行清理栈(如 x64 的默认协定)。

  1. 寄存器如何使用

  • 易变寄存器(Volatile Registers):函数调用时可能被修改的寄存器(如 x64 的RAXRCXRDX),调用方需自行保存这些寄存器的值。

  • 非易变寄存器(Non-Volatile Registers):函数必须保存并恢复的寄存器(如 x64 的RBXRBPR12-R15)。

x86 架构混乱的调用协定

x86 架构发展较早,因此调用协定野蛮生长,有多种调用协定

眼见为实






可以看到,cdecl,stdcall 是通过压栈的方式将参数压入栈中,而 fastcall 直接赋值给寄存器,并无压栈操作

点击查看代码

 
复制代码

x64 的大一统

而在 x64 架构下,为了解决割裂的调用协定,windows 与 linux 实现了统一。

眼见为实



linux 下暂无图(因为我懒),大概就是这意思,自行脑补

点击查看代码

 
复制代码

C#中使用哪种调用协定?



C#在 x86 下,有自己独特的调用协定

在 x64 形成实现统一,与操作系统保持一致

眼见为实





注意寄存器与栈是两片独立运行的区域,光从汇编代码,很容易陷入误区,就拿上图来说,从上往下阅读汇编,你会发现参数传递的顺序是 30(1Eh),40(28h),50(32h),10(0Ah),20(14h)。明显不对,这是因为一个是寄存器,一个是线程栈,这是两个不相关的区域,谁前谁后都不违反从左到右的规定。不能死脑筋,寄存器与栈之间是存在位置无关性的。

/*这种顺序也是正确的,寄存器是寄存器,栈是栈,汇编的顺序不影响他们的位置无关性,因为是两片独立运行的区域*/push 1Ehmov ecx,0Ahpush 28hmov edx,14hpush 32h
复制代码

点击查看代码

 
复制代码

结论

可以看到,在 Windows x64 下,如果方法的参数<=4 那么就就完全避免了栈传递的开销,实现性能最佳化。


在 linux 下,参数为<=6,根据木桶效应,取 4 为最佳。

当然,此文不是让你严格遵守此规则,随着 CPU 性能的发展,在微服务集群大行其道的今天。这点性能差距可以忽略不计,权当饭后消遣,补充冷知识,好让你在未来的Code Review中,没活硬整.

行业拓展

分享一个面向研发人群使用的前后端分离的低代码软件——JNPF

基于 Java Boot/.Net Core 双引擎,它适配国产化,支持主流数据库和操作系统,提供五十几种高频预制组件,内置了常用的后台管理系统使用场景和实用模版,通过简单的拖拉拽操作,开发者能够高效完成软件开发,提高开发效率,减少代码编写工作。

JNPF 基于 SpringBoot+Vue.js,提供了一个适合所有水平用户的低代码学习平台,无论是有经验的开发者还是编程新手,都可以在这里找到适合自己的学习路径。

此外,JNPF 支持全源码交付,完全支持根据公司、项目需求、业务需求进行二次改造开发或内网部署,具备多角色门户、登录认证、组织管理、角色授权、表单设计、流程设计、页面配置、报表设计、门户配置、代码生成工具等开箱即用的在线服务。

用户头像

摸个鱼,顺便发点有用的东西 2023-06-19 加入

互联网某厂人(重生版)

评论

发布
暂无评论
为什么说方法的参数最好不要超过4个?_秃头小帅oi_InfoQ写作社区