[不同编译器]函数参数求值顺序分析(附前置与后置++执行效率)
[不同编译器]函数参数求值顺序分析(附前置与后置++执行效率)
函数参数的求值顺序
?
函数参数的求值顺序跟不同语言编译器的处理方式有关,有些语言编译器从左向右处理,而有时从右往左处理.
这种编译器求值顺序的不同会相应地产生不同的输出结果.
测试程序:
向测试方法method(int a,int b)中传入类似于++x,x+y,x++的实参.
e.g. method(++x,x+y),method(x+y,++x),...
根据输出结果的不同推出参数求值顺序.
一. C语言编译环境
---------------------------------------前绪分析---------------------------------------
?
前置++程序:
?
00401041?? call??????? printf (00401090)
00401046?? add???????? esp,8
12:?????? printf("%d",x);
00401049?? mov???????? edx,dword ptr [ebp-4]
0040104C?? push??????? edx
0040104D?? push??????? offset string "%d" (00422fa4)
00401052?? call??????? printf (00401090)
00401057?? add???????? esp,8
13:?? }?
汇编指令解释:
第十条语句: int x = 2;对应汇编信息是mov dword ptr [ebp-4],2(dword ptr [ebp-4]是变量x的存储单元,语句是将值2放到变量x的存储单元中).
第十一条语句(总共8条指令):
0040102F~00401038: 变量x存储单元中的数值存入ax寄存器,然后ax寄存器中数值加1,再将ax寄存器中的数值存入变量x的存储单元,最后将变量x的存储单元中的数值存入cx寄存器中(总共4条指令)
0040103B: 将cx寄存器中的数值压入栈,作为函数的第二个实参值
0040103C: 将字符串"%d"压入栈,作为函数第一个实参值
00401041: 调用printf函数,执行函数过程?
?
后置++程序:
0040103E?? mov???????? edx,dword ptr [ebp-4]
00401041?? add???????? edx,1
00401044?? mov???????? dword ptr [ebp-4],edx
00401047?? call??????? printf (00401090)
0040104C?? add???????? esp,8
12:?????? printf("%d",x);
0040104F?? mov???????? eax,dword ptr [ebp-4]
00401052?? push??????? eax
00401053?? push??????? offset string "%d" (00422fa4)
00401058?? call??????? printf (00401090)
0040105D?? add???????? esp,8
13:?? }??
汇编指令解释:
第十条语句: int x = 2;对应汇编信息是mov dword ptr [ebp-4],2(dword ptr [ebp-4]是变量x的存储单元,语句是将值2放到变量x的存储单元中).
第十一条语句(总共10条指令):
0040102F~00401035: 变量x存储单元中的数值存入ax寄存器,再将ax寄存器中的数值存入变量x的存储单元的下一个存储单元,最后将变量x的存储单元的下一个存储单元中的数值存入cx寄存器中(总共3条指令)
00401038: 将cx寄存器中的数值压入栈,作为函数的第二个实参值
0040103C: 将字符串"%d"压入栈,作为函数第一个实参值
0040103E~00401044: 将变量x的存储单元中的数值存入dx寄存器,再将dx寄存器中的数值加1,最后最dx寄存器中的数值存入变量x的存储单元中
00401047: 调用printf函数,执行函数过程?
结论(C语言编译环境下):
① 前置++与后置++程序不同就在于一个是++x,另一个是x++;
这一区别造成了执行过程相差2条指令.
由此可见,++x的执行效率高于x++.另外,前置++先执行x=x+1,然后将x值压入栈,做为函数实参值,调用函数;而后置++先将x值压入栈,做为函数实参值,然后执行x=x+1,再调用函数.② 二个程序的printf函数的都是先处理第二个实参,压入栈;然后处理第一个实参,压入栈.
由此可见,C语言编译环境下函数求值顺序是从右向左.
栈是一个先入后出的数据结构,最后一个实参先入栈,则会最后一个出栈,这样在函数从左向右顺序依次读取形参的时候,做出栈操作给对应的形参赋值.?
---------------------------------------前绪分析---------------------------------------
?
?
?
?
?
C语言测试程序:
?
初始化时x = 2,假设:
?① 假设求值顺序是从左向右,则输出为2?3?3?4
?② 假设求值顺序是从右向左,则输出为3?3?2?4?
输出结果:
2?3?3?4?
结论(Java语言编译环境下):
① 由此可知,Java编译环境对函数求值顺序是按照函数参数声明顺序(符合我们正常思维逻辑)的从左向右.
解释:
虚拟机为每一个调用的Java(非本地)方法建立一个新的栈帧.栈帧包括:为方法的局部变量所预留的空间,该方法的操作数栈,以及特定虚拟机实现需要的其他所有的信息.局部变量和操作数栈大小在编译时计算出来,并设置到class文件中去,然后虚拟机就能够了解到方法的栈帧需要多少内存.当虚拟机调用一个方法的时候,它为该方法创建恰当大小的栈帆,再将新的栈帧压入Java栈.
值得一提的Java编译器将Java方法的参数严格按照它们的声明顺序放到"局部变量数组"中.
处理方法时,虚拟机从所调用帧内的操作数栈中弹出实参值.虚拟机把对应的值存入局部变量数组参数对应位置,这种存入可以随机存入.
而C语言需要将后面参数得先入栈(先入后出结构),然后需要参数值的时候出栈供方法使用.
这种不同导致编译器求值顺序的差异.?
② 同C语言编译环境下前置后置++执行过程类似,在Java编译环境函数从左向右顺序求值情况下,++x在执行完x=x+1操作后才做传值操作,这就导致后面的x++中的x值变成3;表达式的输出结果也是3,不难看出x++是先做传值操作再完成的x=x+1操作,这一情况体现在下一条语句:System.out.print("\t" + x);中,x输出的是4.
但是由于本人能力不够,不知道如何查看Java程序编译后类似于C语言汇编后的指令代码.因此,对于Java语言中二者执行效率孰高孰低不敢妄自定夺.姑且类推猜测前置++效率高于后置++.?
?
本人才疏学浅,汇编描述以及Java编译环境结论①解释的地方有不准确的地方,望海涵.
?
?