昨天仙儿去面试,告诉我他被问到这样一个题目。很遗憾尽管这两个函数是如此简单常用,而我也回答错了。换句话说如果我去回答我也会被鄙视的。我以为既然有人这样问,那么可能在内存重叠时可能会不一样,然而我回去查了下 msdn 发现两者对于内存重叠都是未定义行为,所以内存重叠时应该使用 memmove,而不是 memcpy。
那么两者的区别在哪里呢,首先我们忽略参数的类型差别(前者明确要求 char*,后者无要求),因为这个差别非常的无关紧要。答案就是:当拷贝的字节数小于等于字符串长度时,两者的结果是等效的。但当拷贝的字节数大于字符串长度时,strncpy 是用 0 去补齐字节数,而不是取自于 source。而 memcpy 呢,显然是完全不 care 它拷贝是什么内容的。
下面我可以通过一段测试代码给出直观的对比:
#include <string.h>
#include <stdio.h>
void printBytes(char *s, int count)
{
int i;
for(i = 0; i < count; i++)
{
_tprintf(_T("%02X "), s);
if((i & 0xF) == 0xF)
_tprintf(_T("\n"));
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int i;
char src[16], dest[16];
memset(src, 0, sizeof(src));
strcpy(src, "abc");
strcpy(src + 4, "defg");
_tprintf(_T("strncpy:\n"));
strncpy(dest, src, 8);
printBytes(dest, 8);
_tprintf(_T("\nmemcpy:\n"));
memcpy(dest, src, 8);
printBytes(dest, 8);
_tprintf(_T("\n"));
getchar();
return 0;
}
当然了,上面的准备 src 的那两行代码也可以这样写:
strcpy(src, "abcdefgh");
src[3] = 0;
代码输出如下(红色数字标记出了两者的行为差异):
strncpy:
61 62 63 00 00 00 00 00
memcpy:
61 62 63 00 64 65 66 67
可以注意到 strncpy 是需要关注字符串的内容的,所以一旦遇到0以后,后面被拷贝的字节就全部是 0。而 memcpy 是不关注被拷贝的内容是什么的,因此从这个角度来说,无论如何,memcpy 都会比 strncpy 高效一点。
最后我们还需要特别提一点别的,strncpy 在被拷贝的字节数小于字符串长度时,是不负责在结尾添加 null-terminator 字符的,所以这时候字符串可能没有结尾。另外两者对于内存重叠都没有做考虑。当 dest 的地址稍微小于 src 的地址时,可能不会出现问题。当 dest 稍微大于 src 时,因为src 可能被覆盖,所以导致结果有问题。
你不能抱怨面试者为什么要出这样一个问题,即使你平时已经无数次的使用过这两个基本的函数。然而如果你不能给出正确回答,那么结论就是,你对这样基本的函数例如 strncpy 的了解不够彻底导致的。所以归结于自己的问题。
昨天和火车上的一个说英语的中国人(他的母亲是中国人)聊了一路一个多小时的英文,哈哈,这还是我第一次在生活中用英文和别人对话这么长时间。我发现很多很简单的单词我当时居然没有想起来,直到事后我才反应过来,因为当你对话时,你是没时间停下来思考这些最简单的单词是什么的,就好像大脑短路了。例如:
缺点:shortcoming;当时我用了flaw。
目的:aim;当时我用了purpose。
原则:principle。
术语:terminology,这个词我都忘了怎么拼了,是临时查字典查的。
自大:这个词我不知道用什么单词。
这些都是我事后才想起来,当时想提到这些时居然会卡壳。。。我们从简体繁体中文聊到技术(因为我知道对方不是技术人员,所以我其实克制了自己在技术方面去展开,只是给他讲到了 C++ 是学习起来比较难的一门语言,以及代码可读性和运行效率之间的关系)聊到中国的问题,聊到房价,租房,政治等等,同时我也给他普及了诸如”80后“,”屌丝“,”富二代“这些术语。他还夸奖我说我的英文在中国人中比较少见的说的比较流利。这次经历主要是对于我说英文是个鼓励,另外也更加让我认识到要继续提高词汇量的重要性