在Python里,有如下函数声明形式:
def f(n, *args) -> None:
pass
其中,*args代表不定参数个数。
在C/C++中,有两种方式也可以实现变长参数的效果,下面直接给出实例并讲解。
第一种
void sum(int n, ...) {
int* p = &n + 1;
while (n--)
cout << *p++ << endl;
}
在C/C++中,不管是stdcall还是_cdecl调用方式,函数参数都是自右向左压入栈中,栈空间在系统(包括但不限于Windows/Linux)内存中的布局可以看作一个倒放开口向下的盒子,每次往盒内放东西都会放到盒子最底部,而内存地址从盒子口往盒子底部依次递增,换言之,元素入栈时,其入栈地址是依次递减的;出栈时,地址是依次增加的。
综上所述,在传入参数的"..."部分,其元素与n的地址是递增的,即若n地址为0x8B,则"..."的地址依次为0x8E,0x92等。
显然,我们可以采用指针获得n的地址,然后将其后移,就能依次得到"..."内的参数值。
(ps:此种方法可能出现段错误)
其实数组也是类似的原理,数组头部有指针指向一块连续的内存区域,只是系统对其解读不同。
第二种
void sum(int start, ...) {
va_list ap;
__crt_va_start(ap, start);
while (start--) {
cout << &__crt_va_arg(ap, int) << endl;
}
__crt_va_end(ap);
}
第二种实质上就是系统对第一种方式的封装,不过我在__crt_va_arg(ap, int)前加上了一个取址符,好让大家看到打印出来的地址。
调用实例如下
int main()
{
sum(5, 1, 2, 3, 4, 5);
cout << endl;
return 0;
}
代码在VS2019 MSVC环境下运行通过。