学 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/