strcpy,memcpy,memmove, atoi 函数实现
Contact me
- Blog -> https://cugtyt.github.io/blog/index
- Email -> cugtyt@qq.com
- GitHub -> Cugtyt@GitHub
本系列博客主页及相关见此处
strcpy函数的实现
来自https://www.cnblogs.com/chenyg32/p/3739564.html
char * strcpy(char *dst,const char *src) //[1]
{
assert(dst != NULL && src != NULL); //[2]
char *ret = dst; //[3]
while ((*dst++=*src++)!='\0'); //[4]
return ret;
}
- const修饰,源字符串参数用const修饰,防止修改源字符串。
- 空指针检查,
- 不检查指针的有效性,说明答题者不注重代码的健壮性。
- 检查指针的有效性时使用assert(!dst && !src); char *转换为bool即是类型隐式转换,这种功能虽然灵活,但更多的是导致出错概率增大和维护成本升高。
- 检查指针的有效性时使用assert(dst != 0 && src != 0); 直接使用常量(如本例中的0)会减少程序的可维护性。而使用NULL代替0,如果出现拼写错误,编译器就会检查出来。
- 返回目标地址
- 忘记保存原始的strdst值。
- ‘\0’
- 循环写成while (dst++=src++);明显是错误的。
- 循环写成while (src!=’\0’) *dst++=src++; 循环体结束后,dst字符串的末尾没有正确地加上’\0’。
为什么要返回char *?
返回dst的原始值使函数能够支持链式表达式。链式表达式的形式如:
int l=strlen(strcpy(strA,strB));
又如:
char * strA=strcpy(new char[10],strB);
返回strSrc的原始值是错误的:
-
源字符串肯定是已知的,返回它没有意义。
-
不能支持形如第二例的表达式。
-
把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)
C函数memcpy自带内存重叠检测功能,下面给出memcpy的实现my_memcpy。
char * strcpy(char *dst,const char *src)
{
assert(dst != NULL && src != NULL);
char *ret = dst;
my_memcpy(dst, src, strlen(src)+1);
return ret;
}
my_memcpy的实现如下
char *my_memcpy(char *dst, const char* src, int cnt)
{
assert(dst != NULL && src != NULL);
char *ret = dst;
if (dst >= src && dst <= src+cnt-1) //内存重叠,从高地址开始复制
{
dst = dst+cnt-1;
src = src+cnt-1;
while (cnt--)
*dst-- = *src--;
}
else //正常情况,从低地址开始复制
{
while (cnt--)
*dst++ = *src++;
}
return ret;
}
memcpy函数的实现
来自https://blog.csdn.net/goodwillyang/article/details/45559925
void *memcpy(void *dest, const void *src, size_t count)
{
char *tmp = dest;
const char *s = src;
while (count--)
*tmp++ = *s++ ;
return dest;
}
现在是一个字节一个字节的拷贝,改进为按照cpu位宽来拷贝:
void * memcpy(void *dst,const void *src,size_t num)
{
int nchunks = num/sizeof(dst); /*按CPU位宽拷贝*/
int slice = num%sizeof(dst); /*剩余的按字节拷贝*/
unsigned long * s = (unsigned long *)src;
unsigned long * d = (unsigned long *)dst;
while(nchunks--)
*d++ = *s++;
while (slice--)
*((char *)d++) =*((char *)s++);
return dst;
}
如果复制的区域和目标区域有重叠怎么办?
void *memcpy(void *dest, const void *src, size_t count)
{
char *d;
const char *s;
if (dest > (src+size)) || (dest < src))
{
d = dest;
s = src;
while (count--)
*d++ = *s++;
}
else /* overlap */
{
d = (char *)(dest + count - 1); /* offset of pointer is from 0 */
s = (char *)(src + count -1);
while (count --)
*d-- = *s--;
}
return dest;
}
memmove函数的实现
来自https://www.cnblogs.com/dupuleng/articles/4538699.html,有了上面的基础,下面的代码就很好理解了。
void *my_memmove( void *dest , const void *src , size_t count)
{
// check parameter
if(0 == count) return NULL;
if(NULL == dest || NULL == src ) return NULL;
char *dest_ ;
char *src_ ;
if(dest < src )
{
dest_ =(char*) dest; src_ = (char*)src;
while( count-- )
{
*dest_++ = *src_++;
}
}
else if( dest > src )
{
dest_ = (char*)dest + count;
src_ = (char*)src + count;
while(count -- )
{
*--dest_ = *--src_;
}
}
return dest;
}
atoi函数的实现
来自https://blog.csdn.net/lanzhihui_10086/article/details/39995869
int atoi_my(const char *str)
{
int s=0;
bool falg=false;
// 找到非空字符
while(*str==' ')
{
str++;
}
// 处理符号
if(*str=='-'||*str=='+')
{
if(*str=='-') falg=true;
str++;
}
// 循环处理
while(*str >= '0' && *str <= '9')
{
s= s * 10 + *str - '0';
str++;
// 溢出
if(s < 0)
{
s = 2147483647;
break;
}
}
// 添加符号
return s * (falg ? -1 : 1);
}