近来在看Bjarne Stroustrup的TCPL(The C++ Programming Language)中文特别版。看到第6章6.2.5的“增量和减量”的一节,其中举的例子,让人非常深刻地领略到C++的优雅、简洁与高效(从这个例子来看是不是高效,有待验证),下面来看看书中的例子:
运算符 ++ 和 -- 对于在循环里增加或减少变量特别有用。例如,以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算法。只要通用,都应该优先选用标准库的功能,而不是自己去拨弄指针和字节。标准库函数可能被在线化(inline),甚至采用特殊的机器指令实现。所以,在相信一段手工打造的代码的性能优于库函数之前,你应该先经过认真的实测。
B.S.对这个例子的分解讲的很详细,还做了不少关于性能方面的分析和建议,非常有用。
运算符 ++ 和 -- 对于在循环里增加或减少变量特别有用。例如,以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算法。只要通用,都应该优先选用标准库的功能,而不是自己去拨弄指针和字节。标准库函数可能被在线化(inline),甚至采用特殊的机器指令实现。所以,在相信一段手工打造的代码的性能优于库函数之前,你应该先经过认真的实测。
B.S.对这个例子的分解讲的很详细,还做了不少关于性能方面的分析和建议,非常有用。