C++不定参数个数的函数比较常见,比如printf,先写一个字符串,在字符串里有0个或多个%,每个%要对应后面一个数据或者字符之类的东西,理论上有多少个%都是没问题的,难不成还要搞几亿个printf的重载吗?
nonono,这个时候就要用到C++一个神奇的符号——“…”
“…”可以取代一系列相同类型的参数,编译器不关系这一串参数有多少个,类型是什么,但要求省略号之前必须有一个确定类型的参数,就比如printf的第一个参数一定是个字符串。
至于对省略号里面参数的调用,需要用到几个看起来比较高大上的东西。
下面以将几个数加和的函数int add(int n,…)为例(n代表后面的参数个数):
首先是一种新的类型va_list,跟int,double,float一样,就是一种数据类型,至于具体是啥在内存中怎么存储的我们不关心。在使用省略号里边参数时第一步需要做的就是声明一个va_list型的变量,这里我们记为arg_ptr(因为这种写法比较普遍吧)。然后是两个函数,一个是va_start,它是一个操作函数,返回值是void,有两个参数,第一个就是arg_ptr,第二个是则是省略号之前的那个参数,这里就是n,它的作用就是将arg_ptr这个类似指针的东西指向省略号中的第一个参数。第二个函数时va_arg,va_arg函数将返回 arg_ptr 所指位置的值,并将 arg_ptr 指向下一个参数
最后还需要用一个函数va_end(arg_ptr)来释放这个va_list。
写出来就是这个样子:
long long add(int n, ...) {
va_list arg_ptr;
va_start(arg_ptr, n);
long long ans = 0;
for (int i = 0; i < n; ++i) {
ans += va_arg(arg_ptr, int);
}
va_end(arg_ptr);
return ans;
}
当然,如果需要的话,你也可以规定省略号内参数的类型:
long long add(int n, int...) {
va_list arg_ptr;
va_start(arg_ptr, n);
long long ans = 0;
for (int i = 0; i < n; ++i) {
ans += va_arg(arg_ptr, int);
}
va_end(arg_ptr);
return ans;
}
了解了省略号的用法,就可以自己写出printf了,大致思路就是将字符串从头到尾进行扫描,如果碰到%就从后面的省略号中拿一个参数。当然真正的printf还是要考虑更多的事情的,比如如果%d的位置从省略号中拿了一个字符串就应该抛出什么样的错误,如果从省略号中拿出了一个浮点数还要在抛出异常的同时考虑将浮点数转化为整数“糊弄”过去,姑且输出一个东西。
当然,上面自己写的printf中还是要使用printf本身的,因为我们不知道printf更底层的东西,我们做的就是把单纯输出括号中东西的printf转化成了能支持%输出的printf仅此而已