博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

++运算符在C/C++与C#/JAVA中的差异

Posted on 2011-06-06 13:46  阿非  阅读(2134)  评论(15编辑  收藏  举报

++ 运算符

The increment operator (++) increments its operand by 1. The increment operator can appear before or after its operand
增量运算符 (++) 将操作数加 1。增量运算符可以出现在操作数之前或之后

The first form is a prefix increment operation. The result of the operation is the value of the operand after it has been incremented.
第一种形式是前缀增量操作。该操作的结果是操作数加 1 之后的值。

The second form is a postfix increment operation. The result of the operation is the value of the operand before it has been incremented.
第二种形式是后缀增量操作。该运算的结果是操作数增加之前的值。



以上是MSDN对++运算符的定义。此定义在C/C++、C#、JAVA (以下简称它们) 中的表现形式是一致的,故i++与++i在相应语言的编译器中的表现也是一样的。
标题“++ 运算符在C/C++、C#、JAVA中的差异”中的差异不是语言本身的差异,而是相应编译器的差异。

刚刚说过,i++ 与 ++i 在它们的编译器中表现是一样的,但i=i++; 与i=++i;呢 i=++i 这个没有分歧,有分歧的是i=i++ 在C/C++ 中

i=i++; i 自增前的值就是这个表达式的值,在表达式执行后 i自增1 如果 i=1 的话,i=i++ 就等于1 ,i=i++ 执行后 i=2。

int i=1;  printf("%d\n",i = i++); printf("%d\n",i);   会打印出1和2 就是说会对i先赋值,然后对i自增1。

而在C#与JAVA中  i=i++ 等于 i 的值,而 i 的值会变化但仍然是之前的值。因为C#与JAVA的编译器会严格遵循运算符优先级,首先对=右侧求值也就是 i 的值,

然后将 i 值入栈(将1入栈),之后优先级的缘故 ++ 在 = 之前执行 i 先自增1 变成2,然后将栈里的1 赋值给 i , i 这时又变回了1。

相对于C/C++的编译器来说C#与JAVA的编译器的做法更容易解释也更合理,在我看生成的汇编代码前,以为C/C++编译器也会严格遵循优先级,

故猜测是将 i 的地址入栈而不是直接将值入栈所以出现的不一致,但看过之后证明我的猜测是错误的,C/C++编译器是将值放在寄存器中,

但与C#和JAVA的编译器不同的是它未严格遵守运算符优先级,就我目前掌握到的这属于C/C++编译器undefined的范畴,

它们的执行逻辑随编译器的不同而不同。所以千万不要在C/C++程序中依赖求值顺序,否则会死的很惨。

看下面一段代码

int j, i=3
j
=(i+3)*++i; 
printf(
"%d\n", i); 
printf(
"%d\n", j);

在知道结果前,不妨先演算一下。结果是4,28 你算对了么? 而在C#与JAVA中则是4,24。

相应的汇编代码  

  int j, i=3;

   
mov    DWORD PTR _i$[ebp], 3            //将3存储到_i$[ebp]中

   j=(i+
3)*++i;

   
mov    eax, DWORD PTR _i$[ebp]      // 将_i$[ebp]的值放寄存器eax  
   
add    eax, 1                                      // eax+1 结果放在eax中 
   
mov    DWORD PTR _i$[ebp], eax      // eax的值放在_i$[ebp]中 
   
mov    ecx, DWORD PTR _i$[ebp]      // 将_i$[ebp]的值放寄存器ecx 
   
add    ecx, 3                                      // ecx+3 结果放在ecx中 
   
imul    ecx, DWORD PTR _i$[ebp]      //  ecx*_i$[ebp] 
   
mov    DWORD PTR _j$[ebp], ecx      //  将ecx的值放到_j$[ebp]中

生成的IL

[2int32 j,[3int32 i

int j, i=
3;

  
IL_000e:  ldc.i4.3         //将3入栈                                         栈中元素3 
  IL_000f:   stloc.3          //对栈顶元素做弹出操作,并存储到相应下标的变量中      栈中无元素 

j=(i+
3)*++i;

  
IL_0010:  ldloc.3          //对相应下标的变量做取值操作,并入栈                  栈中元素3 
  IL_0011:  ldc.i4.3         //将3入栈                                          栈中元素3,3 
  IL_0012:  add              //对栈顶两个元素依次做弹出并相加,结果入栈             栈中元素6 
  IL_0013:  ldloc.3          //对相应下标的变量做取值操作,并入栈                   栈中元素6,3 
  IL_0014:  ldc.i4.1         //将1入栈                                          栈中元素6,3,1 
  IL_0015:  add              //对栈顶两个元素依次做弹出并相加,结果入栈              栈中元素6,4 
  IL_0016:  dup              //对栈顶元素做复制操作并入栈                          栈中元素6,4,4 
  IL_0017:  stloc.3          //对栈顶元素做弹出操作,并存储到相应下标的变量中         栈中元素6,4 
  IL_0018:  mul               //对栈顶两个元素依次做弹出并相乘,结果入栈             栈中元素24 
  IL_0019:  stloc.2          //对栈顶元素做弹出操作,并存储到相应下标的变量中         栈中无元素

这里没有提供相关的JAVA字节码,但由于JVM和CLR都是 Stack-Based VM,所以只是具体指令不一样,指令的运算逻辑与运算方式都是一样的,可以参看IL部分。

我们将j=(i+3)*++i; 换成j=++i*(i+3);看看会发生什么。相应的汇编代码没有任何改变,IL却是大不相同,输出的结果都是4,28

相关的IL 

IL_000e:  ldc.i4.3 
IL_000f:  stloc.3 
IL_0010:  ldloc.3 
IL_0011:  ldc.i4.1 
IL_0012:  add 
IL_0013:  dup 
IL_0014:  stloc.3 
IL_0015:  ldloc.3 
IL_0016:  ldc.i4.3 
IL_0017:  add 
IL_0018:  mul 
IL_0019:  stloc.2 

相信聪明的你一定可以通过之前IL的注解演算出此段IL的运算过程,我就不赘述了。

 

最后是CSDN里某同学的问题 “int index=1;count=10;执行index=(index++) % count;后输出结果为何还是1”

后输出结果为何还是1 指的是输出index还是1

相关IL

[0int32 index,[1int32 count

  
IL_0001:  ldc.i4.1 
  
IL_0002:  stloc.0 
  
IL_0003:  ldc.i4.s   10 
  
IL_0005:  stloc.1 
  
IL_0006:  ldloc.0 
  
IL_0007:  dup 
  
IL_0008:  ldc.i4.1 
  
IL_0009:  add 
  
IL_000a:  stloc.0 
  
IL_000b:  ldloc.1 
  
IL_000c:  rem 
  
IL_000d:  stloc.0 

相关汇编代码   

int index=1,count=10;

    
mov    DWORD PTR _index$[ebp], 1 
    
mov    DWORD PTR _count$[ebp], 10        ; 0000000aH

index = (index++) % count
;

    
mov    eax, DWORD PTR _index$[ebp] 
    
cdq 
    
idiv    DWORD PTR _count$[ebp] 
    
mov    DWORD PTR _index$[ebp], edx 
    
mov    eax, DWORD PTR _index$[ebp] 
    
add    eax, 1 
    
mov    DWORD PTR _index$[ebp], eax

通过上面的IL 你能否回答他的问题呢?