首页
社区
课程
招聘
[原创]为什么不建议函数有太多参数?
发表于: 2022-1-28 00:34 5748

[原创]为什么不建议函数有太多参数?

2022-1-28 00:34
5748

记录一篇今天工作的思考。为什么不建议函数的有太多参数?


 

今天做组内代码评审时,发现同事的代码有一个小问题,一个函数添加了一个参数后有了7个参数,而公司的编码规范要求,函数的参数不许超过6个。

 

后来我就研究了一下,为啥不建议函数有太多参数呢?当然函数参数太多,不利于维护,学习成本比较高。除此之外,函数参数太多对性能也有一定的影响。


观察参数传递方式

我做了一个实验,观察对含有6个、7个、8个参数的函数进行调用时,到底有哪些不同,测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
 
void func6(int p1, int p2, int p3, int p4, int p5,
           int p6)
{
}
 
void func7(int p1, int p2, int p3, int p4, int p5,
           int p6, int p7)
{
}
 
void func8(int p1, int p2, int p3, int p4, int p5,
           int p6, int p7, int p8)
{
}
 
int main()
{
    func6(1, 2, 3, 4, 5, 6);
    func7(1, 2, 3, 4, 5, 6, 7);
    func8(1, 2, 3, 4, 5, 6, 7, 8);
    return 0;
}

我们查看汇编代码,来观察调用时如何传递参数。

 

我们看一下func6的调用,全部通过寄存器传递。

1
2
3
4
5
6
7
0x00005555555551ce <+8>:     mov    r9d,0x6
0x00005555555551d4 <+14>:    mov    r8d,0x5
0x00005555555551da <+20>:    mov    ecx,0x4
0x00005555555551df <+25>:    mov    edx,0x3
0x00005555555551e4 <+30>:    mov    esi,0x2
0x00005555555551e9 <+35>:    mov    edi,0x1
0x00005555555551ee <+40>:    call   0x555555555169 <func6(int, int, int, int, int, int)>

我们看一下func7的调用,参数1~6通过寄存器,参数7通过堆栈传递。

1
2
3
4
5
6
7
8
0x00005555555551f3 <+45>:    push   0x7
0x00005555555551f5 <+47>:    mov    r9d,0x6
0x00005555555551fb <+53>:    mov    r8d,0x5
0x0000555555555201 <+59>:    mov    ecx,0x4
0x0000555555555206 <+64>:    mov    edx,0x3
0x000055555555520b <+69>:    mov    esi,0x2
0x0000555555555210 <+74>:    mov    edi,0x1
0x0000555555555215 <+79>:    call   0x555555555188 <func7(int, int, int, int, int, int, int)>

我们看一下func8的调用,参数1~6通过寄存器,参数7~8通过堆栈传递。

1
2
3
4
5
6
7
8
9
0x000055555555521e <+88>:    push   0x8
0x0000555555555220 <+90>:    push   0x7
0x0000555555555222 <+92>:    mov    r9d,0x6
0x0000555555555228 <+98>:    mov    r8d,0x5
0x000055555555522e <+104>:   mov    ecx,0x4
0x0000555555555233 <+109>:   mov    edx,0x3
0x0000555555555238 <+114>:   mov    esi,0x2
0x000055555555523d <+119>:   mov    edi,0x1
0x0000555555555242 <+124>:   call   0x5555555551a7 <func8(int, int, int, int, int, int, int, int)>

结论

gcc编译器(gcc9),在x64环境下。函数调用时,前6个参数通过寄存器传递,超过6个后面的参数通过堆栈传递。而寄存器传递参数比堆栈传递效率高,所以建议函数参数不要超过6个。

 

学习c++还是要学习一些汇编的,可以解决很多问题啊。


 

最后,东北码农,全网同名,求关注、点赞、转发,谢谢~


[培训]科锐逆向工程师培训第53期2025年7月8日开班!

收藏
免费 2
支持
分享
最新回复 (11)
雪    币: 15698
活跃值: (19008)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
2
关于学习成本比较高这个,确实深有体会,刚接触人工智能的时候用的是sklearn库,里面的每个模型的参数都很多,学的时候真的是一言难尽……
2022-1-28 09:20
0
雪    币: 15922
活跃值: (7158)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
这点影响可以忽略不计?32位函数所有参数好像都是栈传递?
cryptui.dll中的 LocalEnroll LocalEnrollNoDS 多达23个参数!
2022-1-28 09:36
0
雪    币: 7824
活跃值: (4693)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
这里x64里esi edi都用上了,和VS的编译的代码有点区别
2022-1-28 11:15
0
雪    币: 11379
活跃值: (5566)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
以目前硬件的处理能力衡量,这点影响可以忽略。传参一般都是引用或指针传递,尽可能少的复制变量比少传参更能提升效率。
2022-1-28 11:15
0
雪    币: 5676
活跃值: (4389)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
其实,在必要的情况下,参数多除了调用代码比较长以外,并没有什么大的坏处。
相反,如果为了减少参数而专门封装参数数据,更容易写出低效的代码。
真正应该思考的是,参数多是什么造成的?是否有必要调整逻辑,以减少不必要的参数依赖,从而使代码更简单高效。而不是硬性要求必须要让参数少于几个。
另外,寄存器传参也并不一定都高效,参数占用了寄存器,一是影响寄存器分配,二是函数复杂了,内部依然会用栈来释放寄存器,以保证运算需要。
2022-1-28 13:12
1
雪    币: 15922
活跃值: (7158)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
楼上最后一点很关键。用寄存器传参不一定高效。因为用了寄存器,很有可能在调用前要把该寄存器保存,例如 push rdx然后再赋值,这与堆栈传参并没有什么分别,甚至可能还低效,因为要用3条指令(含调用完POP rdx)。
2022-1-28 14:18
0
雪    币: 8197
活跃值: (3302)
能力值: ( LV9,RANK:180 )
在线值:
发帖
回帖
粉丝
8
这种单位请尽早离职。
2022-1-28 16:38
0
雪    币: 1226
活跃值: (15007)
能力值: ( LV12,RANK:380 )
在线值:
发帖
回帖
粉丝
9
我觉得楼上说得对
2022-1-28 17:35
0
雪    币: 1258
活跃值: (1439)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
先写,有问题再说
2022-2-5 10:13
0
雪    币: 3888
活跃值: (4918)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
拿钱办事 按公司要求混日子
2022-2-5 10:56
0
雪    币: 9941
活跃值: (2273)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
12
对于这个建议,要举反倒很简单,
微软的代码数不尽的十几个参数的API
随便找一个 CreateProcessAsUser 就是11个参数

内核下面就更不得了,那随便一个常用的函数的参数就6个以上
比如 NtWriteFile 就9个了

当时,学写驱动时,一看到这么多参数,真的是头大.

所以性能不性能的,都不重要,主要还是对于后面要抄代码的不友好

 





2022-2-5 14:08
0
游客
登录 | 注册 方可回帖
返回