头皮发麻,这函数参数太多了~

2022-02-17 00:00:00 函数 参数 调用 传递

原文链接:https://mp.weixin.qq.com/s/l7dkHHvWi7tXlpH-iKfhbg


大家好,我是bug菌~
过完正月十五就真的开启新的一年了,年初工作上挺忙碌的,各种开会和各种项目计划,忙着树立各种flag。
或许早已习惯了,计划基本都赶不上变化,"梦想很丰满,现实很骨感吧",这就是大部分公司所谓的年初工作仪式感吧。
本文主要是跟谈谈函数参数太多这个问题,其实应该早一点跟大家聊聊的,毕竟牵涉到软件代码的设计。主要还是因为平时很多话题写文章的时候又想不到,遇到了又不一定会想到要写到文章中去,所以迟迟没有涉及到。
那么今天就针对函数参数数量问题做一个分析和编码建议:

1

函数参数的传递

既然本文谈到函数参数的个数问题,首先就需要了解函数参数到底是如何传递的,并且其影响着什么?

函数的调用过程主要是依赖函数调用栈,对于栈的原理很简单-先进后出,而对于函数调用栈主要是在该栈中分配内存以供函数临时使用-(即压栈),而当函数返回便是一个栈回溯的过程-(即出栈)。

所以函数的参数跟局部变量一样都分配到栈上,当然为了提高函数调用速度,相关变量会直接通过寄存器来传递,原理上也是类似的。

在嵌入式开发中经常会有C语言与汇编语言相互调用的编码形式,这样就需要遵循一定的调用原则。对于使用甚广的ARM内核都遵循ARM过程调用标准APCS(ARM Procedure Call Standard)。
该标准中规定了各种寄存器的使用限制、栈的使用规则、以及函数调用过程中的参数传递返回等,大家编程的时候可以参考相关ARM手册进行了解。
对于函数调用优先会采用R0~R3传递参数,且不需要恢复,那么如果超过4个参数则只能通过栈来传递,相比寄存器传递需要耗费更多的时间压栈和出栈
所以这也是为什么有些人经常说函数参数好不要超过4个或者过多的原因。

2

参数太多引起的问题

前面bug菌在谈及参数太多会导致函数调用更加的耗时,因为增加了参数入栈和出栈的时间,特别是一些循环语句中调用参数较多的子函数。

而对性能上的影响,对于大部分朋友所做的项目可能并不是很在意,快点慢点也无所谓,一般用户也体会不到,毕竟现在的芯片性能都还不错,可以为低效的代码买单。

但是参数太多对于编码风格却是大煞风景,更值得注意的是很容易让编码人员犯错。

bug菌之前就遇到一种情况:

 1void Function(int param1,int param2,int param3,int param4,int param5\
2              int param6,int param7,int param8,int param9,int param10)
3{
4
5    ......
6}
7
8int main(void)
9{
10    ......
11
12    Function(Val1,Val2,Val3,Val4,Val5,Val6,Val7,Val8,Val9,Val10);
13    ......
14}


如以上代码,由于参数太多,在传递参数的过程中,传递顺序不小心错乱了,编译仍然可以正常通过,但程序就引入了bug导致出错。

3

如何处理函数参数

那参数多少才算多?

其实并没有严格的界定,因为终大量的参数定义都会在栈上分配,只要不把栈撑爆了,都是允许的。

对于C语言的参数传递,主要是两种方式,一种就是传值和传地址。

如果一个函数内部不依赖于静态全局区参数或者函数外部存储区,数据均来源于函数参数,那么随着需求不断的变化,函数功能更加的丰富多变,参数也将随着变大。

经常听有些朋友说直接把参数封装成结构体来进行传递,这样的处理虽然能够在一定程度上减少函数参数顺序错乱带来的风险,但与各参数分别传递并没有太大的改善。

就类似于如下代码:

1void process(void)
2
3{
4    sPara.m1 = 1;
5    sPara.m2 = 2;
6    sPara.m3 = 3;
7    sPara.m4 = 4;
8    .....
9
10    process(sPara);    
11}

同时我个人还是不建议简单地用来减少参数数量进行数据打包,结构体应当尽量表现业务上的关联性,当然如果你一定在数据之间强加一种关系,那我也没办法。

同时一个函数一般只实现一种功能,不要把太多的功能放到一个函数中,一方面把函数写得特别长,另一方面就是会使得参数特别多,因为这些参数都和特定的功能相关。

似乎单独值传递并不能直接改善参数太多带来的弊端。

那么就必须借助函数以外的存储区来作为相关参数的存储位置,参数仅仅只是索引,更多的参数和信息还需要根据传递的地址或者是索引来获得,这样就降低了调用函数参数太多所带来的负担。

以前也跟大家介绍过一些C语言面向对象程序的设计,所有的数据和方法都会封装到一个结构体对象中,这对于相关方法函数的调用只需要传递相应的对象地址给形参指针,一个指针多大?应该不用我多说了吧。

当然也没有十全十美的事情,子函数引用函数以外存储区可能相对没有那么独立,因为外部存储区可能会发生参数变化,从而导致子程序运行结果发生变化,这一点是值得注意的,特别是对于多线程编程。

      好了,今天就跟大家分享这么多了,都是bug菌平时总结的C语言编程技巧,好好品味一下,如果你觉得有所收获,一定记得点个~



相关文章