分类: C/C++
2015-11-19 09:32:49
可见,++i和--i执行的时候直接改变了i的值,而i++和i--必须在所在的这个语句执行后才能改变i的值,所以i++作为参数时,实际上是这样的过程,
printf("%d",i++);
相当于下面的语句:
int temp=i;
i = (i+1);
printf("%d",temp);
因此上面的代码可以翻译为:
int i=8;
printf("%d,%d,%d,%d",++i,--i,i++,i--);
因此可以翻译为下面的等效代码:
i=8;
temp0=i; //temp0=8;
i--; //7
temp1=i; //temp1=7
i++; //8
--i; //7
++i; //i=8
printf("%d,%d,%d,%d",i,i,temp1,temp0);
所以打印结果是8,8,7,8
可见,上面的行为可以翻译为下面的等效代码(TC2.0):
i=8;
temp0=i; //这时8已经入栈,实际上通过ax寄存器直接压栈里了~~~,不存在temp0)
i--; //i=7
temp1=i; //这时7已经入栈,实际上通过ax寄存器直接压栈里了~~~,不存在temp1)
i++; //i=8
--i; //i=7
temp2=i; //7已经入栈
++i; //i=8
temp3=i; //8已经入栈
printf("%d,%d,%d,%d",temp3,temp2,temp1,temp0);
输出结果是:8,7,7,8
请注意两者的区别主要是,他们是一边处理自增自减并一边随时入栈,还是先处理完所有自增自减之后再最后统一一次性的入栈。
----------------------------------------------------------
【补充】:在这里这样总结他们的区别是不完善的,实际上涉及到(1)缓存 i 的值,(2)++/--运算符的执行,(3)push i 或 缓存值。
这三者之间的顺序问题。以上三者之间的顺序的微小差别都能对结果产生关键影响。请参考本文最后的补充。
--hoodlum1980 @ 2011年10月12日
----------------------------------------------------------
(1)在TC下面属于前者,每执行一个语句,就把i通过ax寄存器马上入栈了,所以参数入栈和i++等语句是交叉交替性进行的。这里的i++和++i的主要区别在于压栈是在i自增之前还是之后。
i++相当于:先入栈,再自增。
++i相当于,先自增,再入栈。
所以我们看到下面的参数:从右到左:
i--: 入栈8,i=7
i++:入栈7,i=8
--i:i=7,入栈7
++i:i=8,入栈8
所以导致栈里面的参数是8,7,7,8,所以打印结果是8,7,7,8.
(2)在上面的VC.net2005中属于后者,是先为i++和i--保存值,然后执行完所有的自增和自减,最后一次性的把所有参数入栈。在这里i++和++i的区别
主要是是否把i的值保存到另一个位置:而且最大不同点在于这里不马上入栈,而是等所有参数处理后统一入栈。
i++:先缓存i的原始值,然后i自增。最后入栈时,用i的原始值入栈。
++i:i自增,不缓存i的原始值。最后入栈时,是更新后的i。
所以我们看到在VC2005.NET中的顺序是:
i--: 缓存8,i=7
i++:缓存7,i=8
--i:i=7
++i:i=8
参数一次性依次入栈:第一个缓存值8,第二个缓存值7,i的当前值8,i的当前值8。
所以这时候栈的数据是:8,8,7,8.(从左到右)。
所以打印结果是:8,8,7,8.
------------------------------------------------------------
点击(此处)折叠或打开
点击(此处)折叠或打开
【总结】
尽管我们分析了上面的汇编代码,但是实际上还是有些云里雾里。不同编译器产生不同的结果的本质原因在于:在处理++,--运算符和入栈之间的顺序关系,同时还需要了解的是,前置的运算符一般在执行本语句之前就执行,因此无需缓存值。而后置运算符通常的处理则通常需要缓存值。由于本问题中是对同一个变量多次使用++,--运算符,且对其入栈,因此运算符和入栈之间的顺序对结果有着关键影响。通过下表可以进行直观的对比:
int i = 8; printf ( "%d,%d,%d,%d\n", ++i, --i, i++, i-- );
|
||
VC6.0 |
VS2005 |
TC2.0 |
tmp1 = i push tmp1 tmp2 = i push tmp2 --i push i ++i push i i++ i--
|
tmp1 = i i— tmp2 = i i++ --i ++i push tmp1 push tmp2 push i push i |
tmp1 = i i— push tmp1 tmp2 = i i++ push tmp2 --i push i ++i push i |
8,7,8,8 |
8,8,7,8 |
8,7,7,8 |
备注:上面的 tmp1,tmp2 在 VC6和 VS2005中是栈上的实际临时变量(它们是由于两个后置运算符而产生的),在TC2.0中是使用AX寄存器中转。
仔细观察上面的表格中可以发现,push 了 4 个 i 的顺序是没有疑问的,由于有后置运算符,所以需要两个临时变量来缓存当前值。结果不同的原因在于,不同编译器,(1)缓存 i 的当前值,(2)处理 ++,--运算符 和 (3)push 语句之间的顺序不同,因此导致结果不同。