strcpy的实现,以及内部重叠问题的解决。memcpy和memove的区别。
目录
三、假如考虑dst和src内存重叠的情况,strcpy该怎么实现
已知strcpy的原型是char * strcpy(char *strDest, const char *strSrc);
(1)实现strcpy函数
(2)解释为什么要返回char*
(3)假如考虑strDest和strSrc内存重叠的情况,strcpy该怎么实现
一、strcpy的代码实现
char * strcpy(char *strDest, const char *strSrc) //[1]
{
assert((strDest != NULL) || (strSrc != NULL)); //[2]
char *address = strDest; //[3]
while((*strDest++ = *strSrc++)!='\0'); //[4]
return address;
}
注意:strDest和strSrc是形参作用域和生命周期仅限于函数体内,虽然*strDest++ = *strSrc++ 一直加加导致指向结束符'\0',但实参指针的地址依然未变。
[1]const修饰
源字符串参数用const修饰,防止修改源字符串。
[2]空指针检查
断言assert是仅在Debug版本起作用的宏,用于检查“不应该发生”的情况,在运行过程中,若assert的参数为假,那么程序会发生中止。如果程序在assert处终止了,并不是说含有该assert的函数有错误,而是调用者出了差错。
(A)不检查指针的有效性,说明答题者不注重代码的健壮性。
(B)检查指针的有效性时使用assert(!strDest && !strSrc);
char *转换为bool即是类型隐式转换,这种功能虽然灵活,但更多的是导致出错概率增大和维护成本升高。
(C)检查指针的有效性时使用assert(strDest != 0 && strSrc != 0);
直接使用常量(如本例中的0)会减少程序的可维护性。而使用NULL代替0,如果出现拼写错误,编译器就会检查出来。
[3]返回目标地址
(A)忘记保存原始的strdst值。
[4]'\0'
(A)循环写成while (*strDest++=*strSrc++);明显是错误的。
(B)循环写成while (*src!='\0') *strDest++=*strSrc++;
循环体结束后,strDest字符串的末尾没有正确地加上'\0'。
二、为什么要返回char *?
返回strDest的原始值使函数能够支持链式表达式。
链式表达式的形式如:
int l=strlen(strcpy(strA,strB));
又如:
char * strA=strcpy(new char[10],strB);
返回strSrc的原始值是错误的。
其一,strSrc已指向'\0'。
其二,把const char *作为char *返回,类型不符,编译报错。
三、假如考虑dst和src内存重叠的情况,strcpy该怎么实现
char s[10]="hello";
strcpy(s, s+1); //应返回ello,
//strcpy(s+1, s); //应返回hhello,但实际会报错,因为dst与src重叠了,把'\0'覆盖了
所谓重叠,就是src未处理的部分已经被dst给覆盖了,只有一种情况:src<dst<=src+strlen(src)-1
(1)src+count<=dst或dst+count<=src即不重叠,这时正常从低字节依次赋值
(2)dst+count>src >dst 这时部分重叠,但不影响赋值拷贝,也可以从低字节开始
(3)src+count>dst>src 这时也是部分重叠,但影响拷贝,应该从高字节逆序开始。
char * strcpy(char *strDest, const char *strSrc)
{
assert((strDest!=NULL)||(strSrc != NULL));
char *address = strDest;
int size = strlen(strDest)+1;
if((strDest >strSrc) &&(strDest <= strSrc + size -1)) //内存重叠,从尾往前复制
{
strSrc = strSrc + size -1;
strDest = strDest +size -1;
while(size--)
{
*strDest-- = *strSrc--;
}
}
else//正常情况,从头往后复制
{
while(size--)
{
*strDest++ = *strSrc++;
}
}
return address;
}
四、strcpy和memcpy
已知strcpy函数的原型是:char* strcpy(char* dest, const char* src);
memcpy提供了一般内存的复制。即memcpy对于需要复制的内容没有限制,因此用途更广。
void * memcpy ( void * destination, const void * source, size_t num );
Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination.
num
Number of bytes to copy.
size_t is an unsigned integral type.
strcpy和memcpy主要有以下3方面的区别。
(1)复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
(2)复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
(3)用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy
void *memcpy(void *pvTo, const void *pvFrom, size_t size)
{
assert((pvTo != NULL) && (pvFrom != NULL));
char *pbTo = (char *)pvTo;//因为memcpy的size是byte字节
char *pbFrom = (char *)pvFrom;
while(size-- > 0)
{
*pbTo ++ = *pbFrom ++;
}
return pvTo;
}
五、memcpy和memove
memcpy,strcpy这两个函数没有对内存重叠进行处理。使用这两个函数的时候只有程序员自己保证源地址与目标地址内存不重叠。或是使用memmov函数(对内存重叠进行了处理)进行拷贝内存。
参考博客:
1.http://www.cnblogs.com/saolv/p/7737310.html strcpy函数的实现
2.https://www.cnblogs.com/carsonzhu/p/5277036.html 写出完整版的strcpy函数及其他如:strcat,strcmp,strstr的函数实现
3.https://blog.****.net/qq_40123329/article/details/88069183 手动实现strcpy并考虑内存重叠
4.https://www.cnblogs.com/huanzxj/p/3522595.html strcpy和memcpy的区别
5.https://www.cnblogs.com/yixiaoyang/archive/2010/12/04/1896464.html c 内存重叠陷阱memcpy,memmov,strcpy
6.https://www.cnblogs.com/luoquan/p/5265273.html memmove 和 memcpy的区别