本帖最后由 slatop@qq.com 于 2013-5-19 21:01 编辑
分析java中函数参数进栈顺序,这个问题纠结了好久。如果一个函数有多个参数,那么哪个参数先进栈,哪个后进呢。
有人会觉得奇怪我为什么要研究这么没用的问题。上一个简单的代码
void func(int a,int b)//函数原型
{
System.out.println("a:"+a+"\nb:"+b);
}
//调用处
int num = 5;
func(++num,num);
问打印结果是多少?
func(num,++num);
问打印结果又是多少?
经测试前者是6,6后者是5,6.
这时大家可能会恍然大悟。哦~原来如此。函数的参数是先算左边右边的。
可是很遗憾的告诉你。错,你大错特错了。别急听我慢慢说
C和C++中函数的默认调用约定是__cdecl,该约定使函数参数自右向左依次压入栈中,为什么是C和C++要使用这种方式呢。查了一下资料发现,C和C++在早期的版本中支持单参数可变参数。也就是如void func(int...num);的形式。
大家都知道先进后出的栈空间原则,当一个函数只有一个可变参数时,参数必须是从右向左的顺序依次压后栈中,这样在栈顶的那个参数才是可变参数的第一个参数。否则将无法找到可变参数。
而函数参数真正入栈的顺序是由代码编译时的编译器决定的,有的编译器默认函数压栈是自左向右的。C与C++为了兼容不同的编译器加了一个规定。所有可变参数的函数,必须有一个固定参数,也就是如 void func(bool flag,int...num)这样以来函数就可以根据那个固定参数的位置找到可变参数的第一个参数,这样以来别的IDE从左向右压栈的情况下也可以使用可变参数。
大家知道java中允许单参数变参函数。比如void func(int...num);
传递的时候可以传递任何个int的参数。根据C和C++的前车之鉴,那么是否可以肯定java中的参数入栈是自右向左的呢?java虚拟机会不会对这样的可变参数做其它的处理?然后又是费尽了九牛二虎之力,查了很多java虚拟机对函数的解析过程,发现java为了更高效对函数没有经行多余的处理过程。
嗯,到这里或许可以肯定java的参数压栈顺序应该是自右向左的。但是为什么上面的两个测试结果却显示参数的运算是从左到右的呢?
带着这个疑问又开始了掘地三尺
在VC++2010下,无论怎么写上面的那个func函数,输出结果竟然都是6.我似乎明白了关键的地方。经查资料得出结论
函数的参数在入栈后并不是马上被计算的。什么时候被计算取决于你的编译器。(鄙视又是编译器)
设想一下在VC++2010下,所有参数全部入栈,然后参数被运算(运算的过程是从右到左的,已使用指针测试)等所有参数运算完毕,然后将所有参数自栈顶依次提出给被调用者。
而jvm将函数参数自右向左依次送入栈中,每入栈一个参数的同时运算他的值,然后自顶向下(自左向右)取出参数给被调用者。如此解释就顺理成章了。
|