《高质量C++编程指南》的作者林锐博士,曾去微软中国研究院面试,而面试官让他写一个简单的strcpy()函数。林博士很惊讶,心想这有什么难的。而半小时后,林博士大汗淋漓地走出了”考场“。

这样一个小不点函数,面试官从三个方面考察:

  • 代码风格
  • 出错处理
  • 算法复杂度分析

由此可见,编写出合格的strcpy()是不容易的,它尤其考验程序员的基本功和思维的严密性。

下面直接给出最终代码,再逐步剖析:

1
2
3
4
5
6
7
8
9
10
char* strcpy(char* des, const char* src)
{
if(des == src)
return src;
assert(des != NULL);
assert(src != NULL);
char* temp = des;
while((*des++ = *src++) != '\0');
return temp;
}
  • 参数类型:src的内容拷贝到des中,并不会修改src。所以为了防止误操作改变src ,需要声明为const char*
  • 自赋值:dessrc相同时,直接返回任意一者。此处的自赋值并不是像实现string类的赋值函数中的那样必要,但无疑,当两者相同时这样处理更高效。
  • 检查空指针: 这是绝对必要的一步,不检查会引起崩溃。为什么使用 assert 而不是 if ,可见详解if与assert。那么为什么要用两行assert 而不直接 assert((dest!=NULL) && (src !=NULL))?因为前者能定位出错的位置,而后者并不能定位到两个指针中哪个为NULL。
  • 保存原始des值: des的指向已经更改,不能直接返回。
  • 返回值为什么是char* 为了支持链式赋值:int len = strlen(strcpy(des,src));

很多人疑惑为什么不考虑内存重叠(src低于des)的情况,下面是我的猜测:

  1. strcpy()不同于memcpy(),后者能应对内存重叠是因为它有第三个参数n(开辟的字节数),如果发生内存重叠,则可以从高位往低位拷贝,这个过程必须要用n来计数。而前者没有参数n,只能依靠字符串结束符'\n' 来判别终点,所以无法从高位往低位拷贝。
  2. 也许某些时候不要考虑太高的容错性,这样会隐藏人为过失,导致最后bug难以溯源。灵活性 != 容错性。

有错误烦请读者指出。