与在前面:++(–)有太多让人困惑的地方,(i++)+(i++)与(++i)+(++i)有什么不同?为什么不同?如果从机器的角度去理解,就会豁然开朗。
先来看段程序:
int
main()
{
int
i
=
3
;
int
j
=
(i
++
)
+
(i
++
);
//
int j=(++i)+(++i);
printf(
”
%d,%d\n
”
,i,j);
}
(1)在VC 6.0下:
对于(i++)+(i++):
结果:i=5,j=6
相应的汇编代码为(有详细注释):
8B
45
FC mov eax,dword ptr [ebp
–
4
] ;i
->
eax
03
45
FC add eax,dword ptr [ebp
–
4
] ;i
+
i
=
6
89
45
F8 mov dword ptr [ebp
–
8
],eax ;
6
->
j
8B 4D FC mov ecx,dword ptr [ebp
–
4
] ;i
->
ecx(
=
3
)
83
C1
01
add ecx,
1
;ecx
=
4
89
4D FC mov dword ptr [ebp
–
4
],ecx ;
4
->
i
8B
55
FC mov edx,dword ptr [ebp
–
4
] ;i
->
edx
83
C2
01
add edx,
1
;edx
=
5
89
55
FC mov dword ptr [ebp
–
4
],edx ;
5
->
i
对于(++i)+(++i):
结果:i=5,j=10
相应的汇编代码为:
8B
45
FC mov eax,dword ptr [ebp
–
4
] ;i
->
eax (
=
3
)
83
C0
01
add eax,
1
;eax
=
4
89
45
FC mov dword ptr [ebp
–
4
],eax ;
4
->
i
8B 4D FC mov ecx,dword ptr [ebp
–
4
] ;i
->
ecx
83
C1
01
add ecx,
1
;ecx
=
5
89
4D FC mov dword ptr [ebp
–
4
],ecx ;
5
->
i
8B
55
FC mov edx,dword ptr [ebp
–
4
] ;i
->
edx
03
55
FC add edx,dword ptr [ebp
–
4
] ;edx
=
10
,即i
+
i
89
55
F8 mov dword ptr [ebp
–
8
],edx ;
10
->
j
(2)在gcc 3.2.2下:
对于(i++)+(i++):
结果:i=5,j=6相应的汇编代码为:
c7 45 fc 03 00 00 00 movl $3, -4(%ebp) ;3->i
8b 55 fc movl -4(%ebp), %edx ;i->edx (=3)
8b 45 fc movl -4(%ebp), %eax ;i->eax (=3)
8d 04 10 leal (%eax,%edx), %eax ;i+i=6 ->eax
89 45 f8 movl %eax, -8(%ebp) ;6->j
8d 45 fc leal -4(%ebp), %eax ;&i->eax
ff 00 incl (%eax) ;i++ ,即i=4,注意这里为寄存器间接寻址
8d 45 fc leal -4(%ebp), %eax ;&i->eax
ff 00 incl (%eax) ;i++,即i=5
对于(++i)+(++i):
结果:i=5,j=10
相应的汇编代码为:
movl $3, -4(%ebp) ;3->i
leal -4(%ebp), %eax ;&i->eax
incl (%eax) ;i++,即i=4
leal -4(%ebp), %eax ;&i->eax
incl (%eax) ;i++, i=5
movl -4(%ebp), %eax ;i->eax, eax=5
addl -4(%ebp), %eax ;i+i ->eax ,eax=10
movl %eax, -8(%ebp) ;10->j
可见,对于VC6.0和gcc,二者的结果一致,但是gcc 3.2.2生成的汇编代码明显比VC6.0高效、简洁。这也许是因为VC 6.0出现较早的原因吧。
(3)如果这段代码用java实现,结果会怎样呢?
程序:
public
class
TestAdd {
public
static
void
main(String[] args) {
int
i
=
3
;
int
j
=
(i
++
)
+
(i
++
);
//
5,7
//
int j=(++i)+(++i);
//
5,9
System.out.println(i
+
”
,
”
+
j);
}
}
对于(++i)+(++i):
i=5,j=9。结果点意外!
来看看它的字节码吧:
//j=(++i)+(++i)
//
5
,
9
0
: iconst_3
;
常量3入栈
1
: istore_1
;
从栈中弹出3,存入i,i=3
2
: iinc
1
,
1
;
i++, i=4
5
: iload_1
;
将i压入栈,即4入栈
6
: iinc
1
,
1
;
i++,i=5
9
: iload_1
;
i入栈,即5入栈
10
: iadd
;
从栈中弹出两个int类型的数相加,结果入栈,即9入栈
11
: istore_2
;
从栈中弹出9,存入j,即j=9
对于(i++)+(i++):
i=5,j=7。结果也很意外!
也来看看它的字节码吧:
//j=(i++)+(i++)
//
5
,
7
0
: iconst_3
;
常量3入栈
1
: istore_1
;
从栈中弹出3,存入i,i=3
2
: iload_1
;
i入栈,即3入栈
3
: iinc
1
,
1
;
i++,即i=4
6
: iload_1
;
i入栈,即4入栈
7
: iinc
1
,
1
;
i++,即i=5;注意:5没有入栈,所以此时栈中的数为3和4
10
: iadd
;
从栈弹出两个int类型数相加,结果入栈,即7入栈
11
: istore_2
;
从栈中弹出7,存入j,即j=7
Java与VC/gcc为什么会有如此的区别呢?其实原因很简单,VC/gcc生成的是本地代码,而X86处理器是基于寄存器的架构,也就是如果它要进行了两个数相加,它会先把两个数移到寄存器,再进行加法运算。而Java虚拟机是一种基于栈的架构,如果它要进行两个数相加,它会先弹出两个数,再进行加法运算,再将结果入栈。