主要考虑的情况如下:
1、拷贝的数据是任意类型数据,所以指针用void * 接收。
2、src源数据一般是只读的,在函数内部对其修改是非法的。
3、返回值类型是void *,主要用来实现链式表达式,就像赋值表达式一样。
4、判断指针是否为NULL时用assert而不是用if条件语句,因为每次调用函数都会进行一次判断,性能损失较大。
因此我们可以利用宏的开关作用。如果在调试时我们加入“#define DEBUG”语句,增强程序的健壮性,那么在调试通过后我们再改为“#undef DEBUG”语句,提高程序的性能。事实上在标准库里已经存在类似功能的宏:assert,而且更加好用,它还可以在定义DEBUG时指出代码在那一行检查失败,而在没有定义DEBUG时完全可以把它当作不存在。
5、考虑内存重叠的发生,即源地址区间和目的地址区间有重叠的地方。
1)dst <= src || (char )dst >= ((char )src + count
2)dst > src
#include <iostream>
#include <cassert>
using namespace std;
void* MyMemcpy(void* dst, const void* src, int count)
{
assert(dst);
assert(src);
void* ret = dst;
//考虑内存重叠
if (dst <= src || (char*)dst >= ((char*)src + count))
{
while (count--)
{
*(char*)dst = *(char*)src;
dst = (char*)dst + 1;
src = (char*)src + 1;
}
}
else
{
// ret = (char*)src;//保证ret与dst一致
dst = (char*)dst + count - 1;
src = (char*)src + count - 1;
while (count--)
{
*(char*)dst = *(char*)src;
dst = (char*)dst - 1;
src = (char*)src - 1;
}
}
return ret;
}
int main()
{
char p1[256] = "hello world!";
char p2[256] = {0};
// MyMemcpy(p2, p1, strlen(p1) + 1);
// cout << p2 << endl;
// MyMemcpy(NULL, p1, strlen(p1) + 1);
// MyMemcpy(p2, NULL, strlen(p1) + 1);
// MyMemcpy(p1, p1+1, strlen(p1) + 1);
// cout << p1 << endl;
char* ptr = (char*)MyMemcpy(p1+1, p1, strlen(p1) + 1);
cout << p1 << endl;
cout << ptr << endl;
return 0;
}
总结:
初写代码的时候,往往考虑的是程序正常工作的情况该怎么处理。当你有了几年经验,写了几万行代码后就会发现,处理异常部分的分支代码有时比正常的主干线代码还要多,而这也正是高质量程序和一般程序拉开差距的地方。如果把软件产品当作一台机器,那么这样一个个细小的函数和类就是零部件,只有当这些零部件质量都很高时,整个软件产品的质量才会高,不然就会像前几年的国产轿车一样,今天这个零件罢工明天那个零件休息。而作为检验这些零部件的测试用例,一定要模拟各种恶劣的环境,将零部件隐藏的缺陷暴露出来,从这意义上说,编写测试用例的程序员要比软件设计的程序员思维要更严谨才行。