关于i++和++i的思考
概要:
i++ 返回原来的值,++i 返回加1后的值。
JAVA中:
java的编译器在遇到i++和i--的时候会重新为变量运算分配一块内存空间,以存放原始的值,而在完成了赋值运算之后,将这块内存释放掉。下面展示j=i++的过程:
java中二者都只能作为右值,不能作为左值。
C++中:
i++ 不能作为左值,而++i 可以。左值与右值的根本区别在于是否允许取地址&运算符获得对应的内存地址。
int& int::operator++() //这里返回的是一个引用形式,就是说函数返回值也可以作为一个左值使用
{//函数本身无参,意味着是在自身空间内增加1的
*this += 1; // 增加
return *this; // 取回值
}
//后缀形式:
const int int::operator++(int) //函数返回值是一个非左值型的,与前缀形式的差别所在。
{//函数带参,说明有另外的空间开辟
int oldValue = *this; // 取回值
++(*this); // 增加
return oldValue; // 返回被取回的值
}
JAVA中的拓展:
在多线程环境中,虽然在Java中++i是一条语句,字节码层面上也是对应iinc这条JVM指令,但是从最底层的CPU层面上来说,++i操作大致可以分解为以下3个指令:
- 取数
- 累加
- 存储
其中的一条指令可以保证是原子操作,但是3条指令合在一起却不是,这就导致了++i语句不是原子操作。如果变量i用volatile修饰是否可以保证++i是原子操作呢,实际上这也是不行的。至于原因,参考volatile。如果要保证累加操作的原子性,可以采取下面的方法:
- 将++i置于同步块中,可以是synchronized或者J.U.C中的排他锁(如ReentrantLock等)。
- 使用原子性(Atomic)类替换++i,具体使用哪个类由变量类型决定。如果i是整形,则使用AtomicInteger类,其中的AtomicInteger#addAndGet()就对应着++i语句,不过它是原子性操作。