int a=1;
int b,c;
b=(a++)+(a++)+(a++);
//计算结果:b=3,a=4
int a=1;
c=(++a)+(++a)+(++a);
//计算结果:c=10,a=4
为什么c=10呢?
结果与编译器有关,gcc得出的结果也是10。
下面是一个测试的源代码:
int main(int argc, char argv[])
{
int a = 1;
int b,c;
b = (a++)+(a++)+(a++);
printf("b = %d, a = %d./n", b, a); // b = 3, a = 4.
a = 1;
c = (++a) + (++a) + (++a);
printf("c = %d, a = %d./n", c, a); // b = 10, a = 4
return 1;
}
下面是在Linux环境下通过反汇编得到的源程序和汇编代码混合的文件,这里只给出main函数。
int main(int argc, char argv[])
{
8048328: 55 push %ebp
8048329: 89 e5 mov %esp,%ebp
804832b: 83 ec 18 sub $0x18,%esp
804832e: 83 e4 f0 and $0xfffffff0,%esp
8048331: b8 00 00 00 00 mov $0x0,%eax
8048336: 29 c4 sub %eax,%esp
int a = 1;
8048338: c7 45 fc 01 00 00 00 movl $0x1,0xfffffffc(%ebp) // 0xfffffffc(%ebp) 存放的是临时变量a
int b,c;
b = (a++)+(a++)+(a++);
804833f: 8b 55 fc mov 0xfffffffc(%ebp),%edx // a->edx
8048342: 8b 45 fc mov 0xfffffffc(%ebp),%eax // a->eax
8048345: 01 c2 add %eax,%edx // a + a -> edx, eax = a
8048347: 8b 45 fc mov 0xfffffffc(%ebp),%eax
804834a: 8d 04 10 lea (%eax,%edx,1),%eax // ?
804834d: 89 45 f8 mov %eax,0xfffffff8(%ebp) // b = (a++)+(a++)+(a++).这里结果存放在临时变量b
8048350: 8d 45 fc lea 0xfffffffc(%ebp),%eax // eax 指向临时变量a,这里执行的是三个a++.
8048353: ff 00 incl (%eax)
8048355: 8d 45 fc lea 0xfffffffc(%ebp),%eax
8048358: ff 00 incl (%eax)
804835a: 8d 45 fc lea 0xfffffffc(%ebp),%eax
804835d: ff 00 incl (%eax)
printf("b = %d, a = %d./n", b, a);
804835f: 83 ec 04 sub $0x4,%esp
8048362: ff 75 fc pushl 0xfffffffc(%ebp)
8048365: ff 75 f8 pushl 0xfffffff8(%ebp)
8048368: 68 64 84 04 08 push $0x8048464
804836d: e8 f6 fe ff ff call 8048268 <_init+0x38>
8048372: 83 c4 10 add $0x10,%esp
a = 1;
8048375: c7 45 fc 01 00 00 00 movl $0x1,0xfffffffc(%ebp) // 临时变量 a被赋予初值1
c = (++a) + (++a) + (++a);
804837c: 8d 45 fc lea 0xfffffffc(%ebp),%eax // eax指向临时变量a
804837f: ff 00 incl (%eax) // ++a
8048381: 8d 45 fc lea 0xfffffffc(%ebp),%eax // eax指向临时变量a
8048384: ff 00 incl (%eax) // ++a
8048386: 8b 45 fc mov 0xfffffffc(%ebp),%eax // 此时a应该等于3
8048389: 8b 55 fc mov 0xfffffffc(%ebp),%edx // edx指向临时变量a
804838c: 01 c2 add %eax,%edx // edx = eax + edx
804838e: 8d 45 fc lea 0xfffffffc(%ebp),%eax // ++a
8048391: ff 00 incl (%eax)
8048393: 89 d0 mov %edx,%eax // edx->eax
8048395: 03 45 fc add 0xfffffffc(%ebp),%eax
8048398: 89 45 f4 mov %eax,0xfffffff4(%ebp) // 结果存放到c中去
printf("c = %d, a = %d./n", c, a);
804839b: 83 ec 04 sub $0x4,%esp
804839e: ff 75 fc pushl 0xfffffffc(%ebp)
80483a1: ff 75 f4 pushl 0xfffffff4(%ebp)
80483a4: 68 75 84 04 08 push $0x8048475
80483a9: e8 ba fe ff ff call 8048268 <_init+0x38>
80483ae: 83 c4 10 add $0x10,%esp
return 1;
80483b1: b8 01 00 00 00 mov $0x1,%eax
}
80483b6: c9 leave
80483b7: c3 ret
可以看出:
1。第1个式子b = (a++)+(a++)+(a++);的编译器的编译结果是:
先将3个a的值相加,结果存放在临时变量b中。然后,三次增加临时变量a的值。
int a = 1;
8048338: c7 45 fc 01 00 00 00 movl $0x1,0xfffffffc(%ebp) // 0xfffffffc(%ebp) 存放的是临时变量a
int b,c;
b = (a++)+(a++)+(a++);
804833f: 8b 55 fc mov 0xfffffffc(%ebp),%edx // a->edx
8048342: 8b 45 fc mov 0xfffffffc(%ebp),%eax // a->eax
8048345: 01 c2 add %eax,%edx // a + a -> edx, eax = a
8048347: 8b 45 fc mov 0xfffffffc(%ebp),%eax
804834a: 8d 04 10 lea (%eax,%edx,1),%eax // ?
804834d: 89 45 f8 mov %eax,0xfffffff8(%ebp) // b = (a++)+(a++)+(a++).这里结果存放在临时变量b
8048350: 8d 45 fc lea 0xfffffffc(%ebp),%eax // eax 指向临时变量a,这里执行的是三个a++.
8048353: ff 00 incl (%eax)
8048355: 8d 45 fc lea 0xfffffffc(%ebp),%eax
8048358: ff 00 incl (%eax)
804835a: 8d 45 fc lea 0xfffffffc(%ebp),%eax
804835d: ff 00 incl (%eax)
2。第2个式子,其做法就不同。
实际做法是:
-。首先执行第一个++a,方法是gcc把a的地址放进寄存器%eax,然后incl(%eax);
然后解析表达式,遇到后面是+号,加法是需要左值和右值的,所以还要计算右值;
-。然后执行第二个++a,方法也是gcc把a的地址放进寄存器%eax,然后incl(%eax);
此时a的值是3。
-。这个时候左值和右值都有了,可以运行加法了,但是这个时候的a变成了3;
这里的加法是a的值分别取到edx和eax中,然后相加,
所以加出来的值就是3+3=6;这个临时值被放进了一个寄存器(这里是%edx);
上面的例子中又+(++a);这个时候再把a的地址放进寄存器%eax,然后incl(%eax)
-。最后把%edx和a的值相加就可以了;
这里的关键是第1个++a中,表达式(++a)的值没有使用临时变量来缓存,到执行加法的时候是直接再从临时变量a取的值。
a = 1;
8048375: c7 45 fc 01 00 00 00 movl $0x1,0xfffffffc(%ebp) // 临时变量 a被赋予初值1
c = (++a) + (++a) + (++a);
804837c: 8d 45 fc lea 0xfffffffc(%ebp),%eax // eax指向临时变量a
804837f: ff 00 incl (%eax) // ++a
8048381: 8d 45 fc lea 0xfffffffc(%ebp),%eax // eax指向临时变量a
8048384: ff 00 incl (%eax) // ++a
8048386: 8b 45 fc mov 0xfffffffc(%ebp),%eax // 此时a应该等于3
8048389: 8b 55 fc mov 0xfffffffc(%ebp),%edx // edx指向临时变量a
804838c: 01 c2 add %eax,%edx // edx = eax + edx
804838e: 8d 45 fc lea 0xfffffffc(%ebp),%eax // ++a
8048391: ff 00 incl (%eax)
8048393: 89 d0 mov %edx,%eax // edx->eax
8048395: 03 45 fc add 0xfffffffc(%ebp),%eax
8048398: 89 45 f4 mov %eax,0xfffffff4(%ebp) // 结果存放到c中去
结论:
这里只是在VC和Linux环境下的gcc中做过验证,其他环境尚不清楚。
结论就是如果需要明确的结果,建议去掉这种可能产生歧义的表达式。
int b,c;
b=(a++)+(a++)+(a++);
//计算结果:b=3,a=4
int a=1;
c=(++a)+(++a)+(++a);
//计算结果:c=10,a=4
为什么c=10呢?
结果与编译器有关,gcc得出的结果也是10。
下面是一个测试的源代码:
int main(int argc, char argv[])
{
int a = 1;
int b,c;
b = (a++)+(a++)+(a++);
printf("b = %d, a = %d./n", b, a); // b = 3, a = 4.
a = 1;
c = (++a) + (++a) + (++a);
printf("c = %d, a = %d./n", c, a); // b = 10, a = 4
return 1;
}
下面是在Linux环境下通过反汇编得到的源程序和汇编代码混合的文件,这里只给出main函数。
int main(int argc, char argv[])
{
8048328: 55 push %ebp
8048329: 89 e5 mov %esp,%ebp
804832b: 83 ec 18 sub $0x18,%esp
804832e: 83 e4 f0 and $0xfffffff0,%esp
8048331: b8 00 00 00 00 mov $0x0,%eax
8048336: 29 c4 sub %eax,%esp
int a = 1;
8048338: c7 45 fc 01 00 00 00 movl $0x1,0xfffffffc(%ebp) // 0xfffffffc(%ebp) 存放的是临时变量a
int b,c;
b = (a++)+(a++)+(a++);
804833f: 8b 55 fc mov 0xfffffffc(%ebp),%edx // a->edx
8048342: 8b 45 fc mov 0xfffffffc(%ebp),%eax // a->eax
8048345: 01 c2 add %eax,%edx // a + a -> edx, eax = a
8048347: 8b 45 fc mov 0xfffffffc(%ebp),%eax
804834a: 8d 04 10 lea (%eax,%edx,1),%eax // ?
804834d: 89 45 f8 mov %eax,0xfffffff8(%ebp) // b = (a++)+(a++)+(a++).这里结果存放在临时变量b
8048350: 8d 45 fc lea 0xfffffffc(%ebp),%eax // eax 指向临时变量a,这里执行的是三个a++.
8048353: ff 00 incl (%eax)
8048355: 8d 45 fc lea 0xfffffffc(%ebp),%eax
8048358: ff 00 incl (%eax)
804835a: 8d 45 fc lea 0xfffffffc(%ebp),%eax
804835d: ff 00 incl (%eax)
printf("b = %d, a = %d./n", b, a);
804835f: 83 ec 04 sub $0x4,%esp
8048362: ff 75 fc pushl 0xfffffffc(%ebp)
8048365: ff 75 f8 pushl 0xfffffff8(%ebp)
8048368: 68 64 84 04 08 push $0x8048464
804836d: e8 f6 fe ff ff call 8048268 <_init+0x38>
8048372: 83 c4 10 add $0x10,%esp
a = 1;
8048375: c7 45 fc 01 00 00 00 movl $0x1,0xfffffffc(%ebp) // 临时变量 a被赋予初值1
c = (++a) + (++a) + (++a);
804837c: 8d 45 fc lea 0xfffffffc(%ebp),%eax // eax指向临时变量a
804837f: ff 00 incl (%eax) // ++a
8048381: 8d 45 fc lea 0xfffffffc(%ebp),%eax // eax指向临时变量a
8048384: ff 00 incl (%eax) // ++a
8048386: 8b 45 fc mov 0xfffffffc(%ebp),%eax // 此时a应该等于3
8048389: 8b 55 fc mov 0xfffffffc(%ebp),%edx // edx指向临时变量a
804838c: 01 c2 add %eax,%edx // edx = eax + edx
804838e: 8d 45 fc lea 0xfffffffc(%ebp),%eax // ++a
8048391: ff 00 incl (%eax)
8048393: 89 d0 mov %edx,%eax // edx->eax
8048395: 03 45 fc add 0xfffffffc(%ebp),%eax
8048398: 89 45 f4 mov %eax,0xfffffff4(%ebp) // 结果存放到c中去
printf("c = %d, a = %d./n", c, a);
804839b: 83 ec 04 sub $0x4,%esp
804839e: ff 75 fc pushl 0xfffffffc(%ebp)
80483a1: ff 75 f4 pushl 0xfffffff4(%ebp)
80483a4: 68 75 84 04 08 push $0x8048475
80483a9: e8 ba fe ff ff call 8048268 <_init+0x38>
80483ae: 83 c4 10 add $0x10,%esp
return 1;
80483b1: b8 01 00 00 00 mov $0x1,%eax
}
80483b6: c9 leave
80483b7: c3 ret
可以看出:
1。第1个式子b = (a++)+(a++)+(a++);的编译器的编译结果是:
先将3个a的值相加,结果存放在临时变量b中。然后,三次增加临时变量a的值。
int a = 1;
8048338: c7 45 fc 01 00 00 00 movl $0x1,0xfffffffc(%ebp) // 0xfffffffc(%ebp) 存放的是临时变量a
int b,c;
b = (a++)+(a++)+(a++);
804833f: 8b 55 fc mov 0xfffffffc(%ebp),%edx // a->edx
8048342: 8b 45 fc mov 0xfffffffc(%ebp),%eax // a->eax
8048345: 01 c2 add %eax,%edx // a + a -> edx, eax = a
8048347: 8b 45 fc mov 0xfffffffc(%ebp),%eax
804834a: 8d 04 10 lea (%eax,%edx,1),%eax // ?
804834d: 89 45 f8 mov %eax,0xfffffff8(%ebp) // b = (a++)+(a++)+(a++).这里结果存放在临时变量b
8048350: 8d 45 fc lea 0xfffffffc(%ebp),%eax // eax 指向临时变量a,这里执行的是三个a++.
8048353: ff 00 incl (%eax)
8048355: 8d 45 fc lea 0xfffffffc(%ebp),%eax
8048358: ff 00 incl (%eax)
804835a: 8d 45 fc lea 0xfffffffc(%ebp),%eax
804835d: ff 00 incl (%eax)
2。第2个式子,其做法就不同。
实际做法是:
-。首先执行第一个++a,方法是gcc把a的地址放进寄存器%eax,然后incl(%eax);
然后解析表达式,遇到后面是+号,加法是需要左值和右值的,所以还要计算右值;
-。然后执行第二个++a,方法也是gcc把a的地址放进寄存器%eax,然后incl(%eax);
此时a的值是3。
-。这个时候左值和右值都有了,可以运行加法了,但是这个时候的a变成了3;
这里的加法是a的值分别取到edx和eax中,然后相加,
所以加出来的值就是3+3=6;这个临时值被放进了一个寄存器(这里是%edx);
上面的例子中又+(++a);这个时候再把a的地址放进寄存器%eax,然后incl(%eax)
-。最后把%edx和a的值相加就可以了;
这里的关键是第1个++a中,表达式(++a)的值没有使用临时变量来缓存,到执行加法的时候是直接再从临时变量a取的值。
a = 1;
8048375: c7 45 fc 01 00 00 00 movl $0x1,0xfffffffc(%ebp) // 临时变量 a被赋予初值1
c = (++a) + (++a) + (++a);
804837c: 8d 45 fc lea 0xfffffffc(%ebp),%eax // eax指向临时变量a
804837f: ff 00 incl (%eax) // ++a
8048381: 8d 45 fc lea 0xfffffffc(%ebp),%eax // eax指向临时变量a
8048384: ff 00 incl (%eax) // ++a
8048386: 8b 45 fc mov 0xfffffffc(%ebp),%eax // 此时a应该等于3
8048389: 8b 55 fc mov 0xfffffffc(%ebp),%edx // edx指向临时变量a
804838c: 01 c2 add %eax,%edx // edx = eax + edx
804838e: 8d 45 fc lea 0xfffffffc(%ebp),%eax // ++a
8048391: ff 00 incl (%eax)
8048393: 89 d0 mov %edx,%eax // edx->eax
8048395: 03 45 fc add 0xfffffffc(%ebp),%eax
8048398: 89 45 f4 mov %eax,0xfffffff4(%ebp) // 结果存放到c中去
结论:
这里只是在VC和Linux环境下的gcc中做过验证,其他环境尚不清楚。
结论就是如果需要明确的结果,建议去掉这种可能产生歧义的表达式。