java和c语言变长参数的底层实现存在着区别
java其实是一个语法糖
类似
private static int sumUp(int... values) { }
其实最后编译时会转化为
实际上是传递了一个数组引用
private static int sumUp(int[] values) { }
所以如果同时写这两个函数,就会编译不通过,他们本质上是一样的
而c语言的变长参数就要说到函数调用约定了
我们都知道 c语言编译之后调用函数的汇编形式如下
push eax push ebx .... call xxx
即将数据压栈之后,调用方法
因为将数据压栈之后栈指针会变化,所以需要有一个清栈操作
清栈方式主要分为3种
cdecl 由调用者处理栈 常用于c/c++语言
即实际上函数内不包括清栈的代码,而是由调用者在函数返回之后进行清栈
类似这样的形式
push eax push ebx call xxx add esp,8
stdcall 由被调用者清理栈 常用于win32api
即在函数结束但还没有返回之前清栈
(某个函数内部) add esp,8 ret
fastcall与stdcall基本类似,但是会优先使用寄存器传递部分参数
前面也提到了c语言使用的是cdecl的方式,由调用者本身清理栈,那么他有什么好处呢?
先考虑如果被调用者清理栈会是什么后果,即stdcall,在此模式下,函数内部的清栈语句是写死的,所以能够接受的参数在一开始就确定了,比如这个函数能够接受两个参数
最后在函数返回之前就会把esp指针加上8
而cdecl由于是调用者清栈,清栈语句是写在函数外面的,所以完全可以达到传几个参数,就清多少栈,比如ab两个地方调用一个函数,a传了两个int,b传了3个int
则a返回时 esp+8,b返回时esp+12
正是由于c语言的使用cdecl调用约定,才可以实现真正意义上的变长参数,和java的变长参数底层实现是完全不一样的