当前位置: 代码迷 >> C语言 >> 求教大家,简单问题,但为什么是这样的结果?(vc6.0)
  详细解决方案

求教大家,简单问题,但为什么是这样的结果?(vc6.0)

热度:176   发布时间:2008-02-25 04:46:25.0
我是用vc.net2005编译你的代码的,反汇编的结果如下:(局部)
                               mov     [ebp+var_8], 8      //i=8
.text:004113E5                 mov     eax, [ebp+var_8]    //  
.text:004113E8                 mov     [ebp+var_D0], eax   //i--之前把i的值保存到,temp[0]=8   
.text:004113EE                 mov     ecx, [ebp+var_8]     
.text:004113F1                 sub     ecx, 1               //i--,(从右向左数第一个参数)
.text:004113F4                 mov     [ebp+var_8], ecx     //i=7
.text:004113F7                 mov     edx, [ebp+var_8]     
.text:004113FA                 mov     [ebp+var_D4], edx   //i++之前把i保存到:temp[1]=7 (这时候已经执行过i--了)
.text:00411400                 mov     eax, [ebp+var_8]    //i++,(从右向左数第二个参数)
.text:00411403                 add     eax, 1
.text:00411406                 mov     [ebp+var_8], eax    //i=8
.text:00411409                 mov     ecx, [ebp+var_8]   
.text:0041140C                 sub     ecx, 1               //--i, 无需保存修改前的值,直接改变实参i
.text:0041140F                 mov     [ebp+var_8], ecx    //i=7
.text:00411412                 mov     edx, [ebp+var_8]
.text:00411415                 add     edx, 1              //++i,
.text:00411418                 mov     [ebp+var_8], edx    //i=8
.text:0041141B                 mov     esi, esp
.text:0041141D                 mov     eax, [ebp+var_D0]   //压栈temp[0]=8
.text:00411423                 push    eax
.text:00411424                 mov     ecx, [ebp+var_D4]   //压栈temp[1]=7
.text:0041142A                 push    ecx
.text:0041142B                 mov     edx, [ebp+var_8]    //压栈i=8
.text:0041142E                 push    edx
.text:0041142F                 mov     eax, [ebp+var_8]    //压栈i=8
.text:00411432                 push    eax
.text:00411433                 push    offset aDDDD    ; "%d,%d,%d,%d\n"  //压栈字符串"%d,%d,%d,%d\n"的地址   
.text:00411438                 call    ds:printf            //调用打印函数,输出8,8,7,8
.text:0041143E                 add     esp, 14h
.text:00411441                 cmp     esi, esp
.text:00411443                 call    sub_41113B
.text:00411448                 mov     esi, esp

可见,++i和--i执行的时候直接改变了i的值,而i++和i--必须在所在的这个语句执行后才能改变i的值,
所以i++作为参数时,实际上先把执行自增前的i值保存起来,i自增,然后用刚才保存的值做参数,是这样的过程,
   printf("%d",i++);
相当于下面的语句:
   int temp=i;
   i=(i+1);
   printf("%d",temp);
而++i作为语句一部分时,可以直接改变i,例如
   printf("%d",++i);
相当于:
   i=(i+1);
   printf("%d",i);

因此上面的代码可以翻译为:
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下面打印结果是:8,7,7,8
而你说你在vc6下面是:8,7,8,8
看来这确实是和编译器有关,三个编译器居然出来的是三个不同结果。而这种代码在实际编程是是不可能写出这么歧义的代码的。。。。。

[[it] 本帖最后由 hoodlum1980 于 2008-2-25 04:52 编辑 [/it]]
----------------解决方案--------------------------------------------------------
---------------------------------------------------------
----------------------TC2.0反汇编结果--------------------
0:01FA sub_1FA         proc near               ; CODE XREF: start+11Ap
seg000:01FA                 push    bp         //保护bp入栈
seg000:01FB                 mov     bp, sp     
seg000:01FD                 push    si         //保护si入栈         
seg000:01FE                 mov     si, 8      //i=8
seg000:0201                 mov     ax, si     //ax=i
seg000:0203                 dec     si         //i--,i=7
seg000:0204                 push    ax         //ax入栈,8入栈
seg000:0205                 mov     ax, si     //ax=i,ax=7
seg000:0207                 inc     si         //i++,i=8
seg000:0208                 push    ax         //ax入栈,7入栈
seg000:0209                 dec     si         //--i,i=7
seg000:020A                 mov     ax, si     //ax=i
seg000:020C                 push    ax         //ax入栈,7入栈
seg000:020D                 inc     si         //++i,i=8
seg000:020E                 mov     ax, si     //ax=i
seg000:0210                 push    ax         //ax入栈,8入栈
seg000:0211                 mov     ax, 194h   
seg000:0214                 push    ax         //194h入栈
seg000:0215                 call    sub_AD9
seg000:0218                 add     sp, 0Ah
seg000:021B                 call    sub_15DB
seg000:021E                 xor     ax, ax
seg000:0220                 jmp     short $+2
seg000:0222                 pop     si        //恢复si
seg000:0223                 pop     bp        //恢复bp
seg000:0224                 retn
seg000:0224 sub_1FA         endp

可见,上面的执行顺序是(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

请注意以前两者的区别主要是,在TC下面没执行一个语句,就把i入栈了,所以参数入栈和i++等语句是交叉交替性进行的。这导致
参数数组呈现随时变化。所以这里的参数跟踪了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.

而在上面的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.
----------------解决方案--------------------------------------------------------
感谢大家的热心帮助,尤其是几位大侠 peach5460   chenyiqing23  hoodlum1980  的鼓励和详细指导,谢谢大家。

[[it] 本帖最后由 默默无纹 于 2008-2-25 08:04 编辑 [/it]]
----------------解决方案--------------------------------------------------------
  相关解决方案