i++ 到底是怎么回事
学 C/C++ 的童鞋应该都知道 ++i 和 i++ 的区别。开始的时候,我们会这样理解:++i 是让 i 自己增加 1 之后再使用 i 的新值,i++ 是在使用 i 的值之后再让 i 自身增加 1. 这样模糊的说法让这个 i 自增 1 的过程变得充满神秘。人类总是对神秘的东西怀有兴趣,探索的过程就是这样开始的。
但是 i 的自增究竟是什么时候进行的,这个问题困扰了我很久。直到我学到 C++ 的运算符和强制类型转换重写的时候,我开始把一切都看成函数。我经常把基础的数据类型看成类,然后为这些类 YY 出一些实现代码,觉得这一切都合情合理和谐美好。这时我对 ++i 和 i++ 的理解就是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class int { /* Some harmonious code */ public: int operator++(int foo) // i++ { foo = *this; *this = *this + 1; return foo; } int &operator++() // ++i { *this = *this + 1; return *this; } }; |
当时我做了些许尝试,发现 i++ 是一个右值表达式,++i 是一个左值表达式,就觉得自己的想法应该是对的。而且,如果 C/C++ 的确是这样“想”的话,把一切运算符都看成函数的话,这一切是多么美好、和谐和统一啊。
然后我又有了一种想法,就是在 for 循环上,使用 ++i 而不是 i++. 为什么呢?从上面代码的实现上看,i++ 是又浪费空间又浪费时间的方法,而我们并不需要在 for 循环上用到 i 以前的值,所以还用个中间变量存储 i 原来的值真是多此一举。所以我在 2008 年以后的代码基本上都是写 ++i 的。
但是最近一堂 C 语言的课上,我的这种和谐统一的愿望破灭了。老师写了类似下面的代码:
1 2 3 | int i = 3; int j = (i++) + (i++) + (i++); printf("%d %d\n", i, j); |
结果输出了 6 9. 而我想的是输出 6 12. 我原以为只有微软的编译器会做这么蛋疼的事,但是后来用 g++ 来编译,效果是一样的。事实说明,这些编译器是把 i++ 放到了整个语句之后再让 i 自增的。蛋疼的编译器把我统一的理想破灭了。
这里顺带提到了逗号运算符和不定个数的函数参数的问题。逗号运算符是由若干个逗号组成的一个表达式,请注意它是一个表达式,它的运算顺序是从左到右,但是表达式的值却是逗号最右边的部分的值。其实这用我的“统一”理论很容易解释,逗号运算符可以看成双目的,多个逗号运算符只是函数的嵌套而已。比如:
1 | a, b, c |
可以表示成函数的形式:
1 | operator,( a, operator,(b, c) ) |
这是多么和谐统一啊。
但是函数的不定参数问题往往和逗号运算符混淆在一起(反正我以前就混淆过……)。比如 printf 问题:
1 2 | int i = 1; printf("%d %d\n", i, ++i); |
结果输出的是 2 2 而不是 1 2. 就是因为不定个数的参数是从右向左求值的。
搞不懂为什么 C/C++ 的标准不规定一下这些,让所有编译器能够统一地实现这些语句……
还有就是,似乎高考前我说我要写有关 C++ 的书的,结果高考后的假期我杯具地颓废了。现在心有余力不足了…… 唉,等我闲得蛋疼的时候再说吧。
原创文章,转载请注明来源:http://euyuil.com/1823/what-the-hell-is-i-plusplus/

这个让人看了DT的i++
很混乱啊…都搞不懂是在语句后执行+1还是语句执行前+1…
很奇怪,理解上来说,我偏向于先+1,再进行函数运算…