### 起源
<br>
在阅读代码的时候,总会遇到过以下类似的写法:
```php
<?php
for($i = 0, $length = 10; $i < $length; ++$i) {
// do something
}
for($i = 0, $length = 10; $i < $length; $i++) {
// do something
}
```
这两个 for 循环最终都会循环 10 次,那么`$i++`和`++$i`的写法有何不同?或者说:**自加(递增)运算符前置与后置到底有什么区别?**
### 解释1st
<br>
如果去查阅 PHP 文档或者 C++ 教程书,一般都会得到以下论述:
> * <font size=3>前置运算符`++i`表示**先运算`i=i+1`,后取值`2`**</font>
> * <font size=3>前置运算符`i++`表示**先取值`i=1`,后运算`i=i+1`**</font>
但这只是解释了前置与后置在实际程序中的体现,并不能知道为什么会有这两种写法。
### 解释2nd
<br>
前置自增和后置自增的另一个区别是,**前置自增`++i`的结果可以作为左值使用,而后置自增`i++`不可以**。
> * <font size=3>左值是对应内存中有确定存储地址的对象的表达式的值,而右值是所有不是左值的表达式的值。 —— 维基百科『右值引用』词条</font>
一般理解是,**左值是可以放到赋值符号左边的变量**。但是能否被赋值不是区分左值与右值的依据。比如,C++的const左值是不可赋值的;而作为临时对象的右值可能允许被赋值<!--(这个比较难理解,需要结合)-->。
> <font size=3>因此,左值与右值的根本区别在于**能否进行取地址运算`&`来获得对应的内存地址**。
> 这也是对应了维基解释中的“左值是对应内存中**有确定存储地址的**对象的表达式的值”</font>
### C/C++ 中的实现
<br>
```cpp
/**
* 前缀形式:
* 这里是以引用方式返回值,返回可以作为左值
* 函数本身无参数,意味着是在自身空间内自增
*/
int& int::operator++()
{
*this += 1;
return *this;
}
/**
* 后缀形式:
* 函数返回值是非左值型的,这是与前缀形式的差别
* 函数本身带参数,说明有另外的空间开辟
*/
const int int::operator++(int)
{
int oldValue = *this;
++(*this); // 此行等同于使用上面定义好的前缀自加,与下行等效
// *this += 1;
return oldValue; // 返回自增前的值
}
```
因此,具有一定经验的 C/C++ 程序员,会认为`i++`比`++i`多了产生临时变量的开销,出于性能考虑会选择写成`++i`。
<!--### 在 for 循环中的表现-->
<!--<br>-->
### 总结
<br>
如果没有用到返回值的话,前置与后置的区别主要在于效率和空间占用的区别
* 若`i`是内置的数值类型,对于现代编程语言来说仅有语义上的区别(参见"解释1st")
* 若`i`是自定义类的实例,例如`iterator`(迭代器),`++i`的效率比`= i++`的效率高
* `i++`更贴近人类思维习惯,`++i`在使用时基本可以不用考虑`i`的类型
### 资料参考 & 致谢
<br>
* [在程序开发中,++i 与 i++的区别在哪里? - 叶王的回答 - 知乎](https://www.zhihu.com/question/19811087/answer/80210083)
* [Java中i++和++i的区别](https://blog.csdn.net/qq_34471736/article/details/54599901)
* [for循环的 i++ 和 ++i 的区别](https://www.sojson.com/blog/78.html)
i++ 与 ++i ——自加运算符前置与后置的问题