增量和减量

  ++运算符用于直接表示增加,使人不必通过加和赋值的组合去间接地表示这个操作。根据定义,++lvalue的意思就是lvalue += 1,这又相当于 lvalue = lvalue + 1,条件是lvalue的求值没有副作用。指明被增加的那个对象的表达式只做一次求值。减量也类似地采用--运算符表示。运算符++和--都可以用做前缀或者后缀运算符。++x的值是(增量之后的)新值。例如,y=++x等价于y=(x+=1)。x++的值则是x原有的值。例如,y=x++等价于y=(t=x, x+=1, t),这里的t是一个与x同类型的变量。

  就像可以对指针做加和减一样,++和--也可以用到对数组元素进行操作的指针上;p++使p指向下一个元素(5.3.1节)。

  运算符++和--对于在循环里增加或减少变量特别有用。例如,以0结尾的字符串可以通过下面方式复制:

    void cpy(char* p, const char* q)
    {
        while(*p++ = *q++);
    }

和C一样,C++也因为允许这类紧凑的面向表达式的编码方式而受到喜爱或者仇恨。因为

while(*p++ = *q++)

对于非C程序员而言是个小障碍,也因为以这种风格编写的代码在C和C++里绝非罕见,因此需要进一步仔细考察。

  首先考虑一下更传统的复制一个字符数组的方式

    int length = strlen(q);
    for(int i = 0; i <= length; i++) p[i] = q[i];

这确实很浪费。要确定以0结尾的字符串的长度,就需要通过读这个串去找到结尾的0。这样我们实际上就读了字符串两次:一次为确定其长度,另一次是复制。试试另一种方式:

    int i;
    for(i = 0; q[i] != 0; i++) p[i] = q[i];
    p[i] = 0;        // 以0结束

用做下标的变量i可以去掉,因为p和q本身就是指针:

    while(*q != 0)
    {
        *p = *q;
        p++;        // 指向下一个字符
        q++;        // 指向下一个字符
    }
    *p = 0;         // 以0结束

后增量运算符使我们可以先用变量的值,而后再增加它。我们可以将循环重新写出:

    while(*q != 0)
    {
        *p++ = *q++;
    }
    *p = 0;          // 以0结束

由于*p++=*q++的值也就是*q,所以我们可以将这个例子重写为:

    while((*p++ = *q++) != 0) {}

在这个情况中,我们只有把*q复制到*p并增加了p之后,才会注意到它的值是不是0。因此我们就可以删除设置结束0的最后一个语句。最后,我们可以看到并不需要空的快,而且“!=0”也是多余的,因为指针的结果或者整数条件实际上都要与0做比较。这样,我们就得到了开始时想找的版本:

    while(*p++ = *q++);

这个版本比前面的版本更难读吗?对有经验的C或C++程序员而言并不是这样。在时间和空间上这个版本比前面的版本更有效吗?除了第一个调用strlen()的版本外也都不一定。哪个版本最有效,也可能依机器结构和编译器的不同而不同。

  要在你的特殊计算机上复制以0结尾的字符串,最有效的方式应该是用标准的串复制函数

char* strcpy(char*, const char*);        // 来自<string.h>

对于更一般的复制,可以使用标准copy算法(2.7.2节、18.6.1节)。只要能用,都应该优先选用标准库的功能,而不是自己去拨弄指针和字节。标准库函数可能在线化(7.1.1节),甚至采用特殊的机器指令实现。所以,在相信一段手工打造的代码的性能优于库函数之前,你应该先经过认真的实测。

🔚