以下内容为自己的学习笔记,如需求转载,请声明原文链接 微信大众号「ENG八戒」mp.weixin.qq.com/s/nj0C9SbAu…

手把手教你完成 C 言语的函数多参默认值 上

自从上一篇关于 C 言语单个参数函数的默认值完成《C言语函数也能够给形参添加默认值?》发表以来,有很多的同学反应想知道多参数函数的默认值又该怎样完成,今天特别整理相关代码和完成思路阐明如下。

因为 C 言语本身是不提供函数默认值功用和相关语法的,为了完成函数参数的默认值,归根到底是对原有函数参数的主动填充,填充的值就是默认值。

那么怎样填充参数值,又不会影响程序性能呢?能够运用宏界说的灵活性在代码的编译预处理期做一些替换操作,本文和《C言语函数也能够给形参添加默认值?》文章的基本思路类似,但是完成上会繁琐一些,终究的方针是完成从单参数默认值向多参数默认值迭代晋级。

设想一下总体思路:

因为带有默认值的函数在运用时,形式上输入的参数个数是不定的,所以需求核算函数实践输入参数的个数,然后针对未输入的参数填充默认值。所以这儿提出两个问题,一个是怎样核算函数实践输入参数的个数,另一个是怎样对参数填充默认值?

接下来将对提出来的问题逐一破解。

核算函数实践输入参数的个数

咱们先来界说一个带有 2 个输入参数的方针函数 _fun2():

void _fun2(int val1, int val2)
{
    printf("fun inputs val1:%d, val2:%dn", val1, val2);
}

参阅《C言语函数也能够给形参添加默认值?》文章的基本思路,界说一个变长参数宏 fun2() 代表方针函数 _fun2(),原来调用方针函数 _fun2() 的语句将变成直接调用宏 fun2(),而方针函数 _fun2() 间接被调用。

但是因为咱们的思路里多了一个因素—函数实践输入参数的个数,所以变长参数宏 fun2() 和方针函数 _fun2() 之间还需求多一层转化,下面统称为转化函数 _funs(),函数实践输入参数(调用宏 fun2() 时输入的参数)的个数作为参数输入到转化函数 _funs(),方针函数 _fun2() 在转化函数 _funs() 内部被调用。

#define fun2(...)           _funs(ARGC(__VA_ARGS__), 123, 456, ##__VA_ARGS__)
void _funs(int real_param_num, ...)
{
    ...
    // _fun2(val1, val2);
}

宏界说 ARGC() 用于核算函数实践输入参数的个数,123 和 456 作为方针函数 _fun2() 的参数默认值。real_param_num 是转化函数 _funs() 的第一个参数,用于接纳变长参数宏 fun2() 被调用时输入的参数个数。

另外,鉴于视乎实践需求,方针函数的参数也能够是 2 之外的其它数目,所以为了便于扩展转化函数 _funs() 的应用范围,需求输入方针函数的完整参数个数,持续迭代转化函数 _funs()。

#define fun2(...)           _funs(2, ARGC(__VA_ARGS__), 123, 456, ##__VA_ARGS__)
void _funs(int param_num_max, int real_param_num, ...)
{
    ...
    // _fun2(val1, val2);
}

用变长参数宏 fun2() 代表有 2 个输入参数的方针函数 _fun2(),param_num_max 指定方针函数的完整参数个数。

ARGC 怎样完成?

为什么运用宏界说来核算函数实践输入参数的个数?

因为 C 运转进程中确认参数个数会耗费系统资源,并下降程序运转功率,所以尽量采用在编译预处理时核算的宏界说。

暂定参数个数上限是 2 个,用宏界说预处理对参数的打开来统计参数个数,涉及占位符的妙用(信任会令你开眼界):

#define __ARGS(X) (X)
#define __ARGC_N(_0,_1,N,...) N
#define __ARGC(...) __ARGS(__ARGC_N(__VA_ARGS__,2,1))
#define ARGC(...)       __ARGC(__VA_ARGS__)

其中 _0, _1,… 是预处理占位符,用于在宏界说打开参数时逐个匹配参数,_0 匹配第一个参数,_1 匹配第二个参数,以此类推,终究 N 用于匹配 __ARGC_N(__VA_ARGS__,2,1) 后边的逆序数字 2 或许 1,这个 N 就是终究核算所得的参数个数。

测验一下对参数个数的核算效果:

printf("ARGC() arg num=%dn", ARGC());
printf("ARGC(1) arg num=%dn", ARGC(1));
printf("ARGC(1, 2) arg num=%dn", ARGC(1, 2));

针对上面的调用,为了更好了解求值进程,让咱们测验一下手动对宏调用 ARGC(1) 打开看看:

ARGC(1)
__ARGC(1)
__ARGS(__ARGC_N(1,2,1))
(__ARGC_N(1,2,1))       ---->  (__ARGC_N(1,2,N))
(1)                     ----> N = 1

ARGC(1) 应该返回 1。

完整测验成果输出:

ARGC() arg num=1
ARGC(1) arg num=1
ARGC(1, 2) arg num=2

为什么 ARGC() 输入 0 个参数时,核算成果不为 0,而输入其它数量的参数核算成果契合预期呢?核算进程还有待改进。

你能够自己手动打开一下 ARGC() 看看,当 ARGC() 输入 0 个参数时,__ARGC_N(__VA_ARGS__,2,1) 后边的逆序数字数量不足以匹配 N,此刻的 N 就强制匹配最后一个数字 1 了。为了处理这种反常匹配,当 N == 1 时,能够追加对第一个输入参数是否为空的判断:

#define __ARGC_N(_0,_1,N,...) N==1?(#_0)[0]!=0:N

#_0 用于将第一个参数转化成字符串,会占用一点空间,但不影响程序运转功率。(#_0)[0] 提取字符串的第一个字节。当 N == 1 时,假如第一个参数是空字符,(#_0)[0]!=0 返回 false,否则返回 true。false 转化成 0,true 转化成 1。

终究测验成果输出:

ARGC() arg num=0
ARGC(1) arg num=1
ARGC(1, 2) arg num=2

假如参数上限是 3 个,又怎样修正呢?

#define __ARGS(X) (X)
#define __ARGC_N(_0,_1,_2,N,...) N==1?(#_0)[0]!=0:N
#define __ARGC(...) __ARGS(__ARGC_N(__VA_ARGS__,3,2,1))
#define ARGC(...)       __ARGC(__VA_ARGS__)

__ARGC_N(__VA_ARGS__,3,2,1) 后边的逆序最大数字 3 就决议了参数的上限个数为 3。当然修正参数上限,__ARGC_N 宏界说输入参数中的占位符 _0,_1,_2 个数需求对应参数上限的个数,占位符从 _0 开端。

假如输入的参数超过了上限呢?

printf("ARGC(1, 2, 3) arg num=%dn", ARGC(1, 2, 3));
printf("ARGC(1, 2, 3, 6) arg num=%dn", ARGC(1, 2, 3, 6));
printf("ARGC(1, 2, 3, 6, 9) arg num=%dn", ARGC(1, 2, 3, 6, 9));

成果输出:

ARGC(1, 2, 3) arg num=3
ARGC(1, 2, 3, 6) arg num=6
ARGC(1, 2, 3, 6, 9) arg num=6

输出的参数个数就等于超出上限的第一个参数值,不过,有言在先,参数个数有上限,而且合理设计的函数参数不应该过多,不必过度计较了。


未完待续,关注我查看更多精彩内容