增量和减量
++运算符用于直接表示增加,使人不必通过加和赋值的组合去间接地表示这个操作。根据定义,++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节),甚至采用特殊的机器指令实现。所以,在相信一段手工打造的代码的性能优于库函数之前,你应该先经过认真的实测。
🔚