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++程序中依赖求值顺序,否则会死的很惨。
看下面一段代码
j = (i + 3 ) *++ i;
printf( " %d/n " , i);
printf( " %d/n " , j);
在知道结果前,不妨先演算一下。结果是4,28 你算对了么? 而在C#与JAVA中则是4,24。
相应的汇编代码
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
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_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的运算过程,我就不赘述了。
最后是优快云里某同学的问题 “int index=1;count=10;执行index=(index++) % count;后输出结果为何还是1”
后输出结果为何还是1 指的是输出index还是1
相关IL
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
相关汇编代码
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 你能否回答他的问题呢?