您的位置:首页 > 其它

strcpy(),memcpy(),memmove(),memset(),strcmp(),strstr()的实现

2013-08-12 16:41 519 查看
在C语言中strcpy的原型是char *strcpy(char *dest,const char *src),它的的功能是把src所指由'\0'结束的字符串复制到dest所指的数组中,下面是实现

char *strcpy(char *dest,const char *src)
{
assert((NULL != dest) && (NULL != src));
char* pDest = dest;
while(*src != '\0')
{
*pDest++ = *src++;
}
return dest;
}

内存拷贝:memcpy(),函数memcpy(),与strcpy()不同,需要考虑内存重叠的可能。当(pSrc<pDst) && ((char*)pSrc+size > pDst)时,如果拷贝是从头开始正向拷贝,就会在拷贝过程中污染pSrc中还未拷贝的数据。因此在这种情况下必须反向拷贝。实际上在C语言库中,与内存复制有关的函数有两个:memcpy()和memmove()。前者没有考虑内存重叠,而后者考虑了内存重叠。所以使用时需要注意。下面是实现代码(我的实现代码加入了处理内存重叠的情况):

void memcpy(void *pDest,const void *pSrc, int size)
{
assert((NULL != pDest) && (NULL != pSrc) && (size>=0));
//内存拷贝,参数都是void类型的指针,所以要将其转换成char*类型的指针以便按字节进行拷贝
char* dest = static_cast<char*> (pDest);
const char* src = static_cast<const char*> (pSrc);
//拷贝的目标地址段与原字符串的地址段有重复,则进行反向拷贝
if(src<dest && (src+size)>dest)
{
dest = dest+size-1;
src = src+size-1;
while(size>0)
{
*dest-- = *src--;
size--;
}
}
else//拷贝的目标地址段与原字符串的地址段无重复,则正向拷贝
{
while(size>0)
{
*dest++ = *src++;
size--;
}
}
}


既然要考虑源地址段与拷贝的目标地址段重复的问题,那么为了便于记忆,我们可以不管它是否源地址段与目标地址段重复,我们都可以进行反向拷贝(????????这里是有问题的,见下面memmove()函数的分析)。

void memcpy(void *pDest,const void *pSrc, int size)
{
assert((NULL != pDest) && (NULL != pSrc) && (size>=0));
//内存拷贝,参数都是void类型的指针,所以要将其转换成char*类型的指针以便按字节进行拷贝
char* dest = static_cast<char*> (pDest);
const char* src = static_cast<const char*> (pSrc);

//为了便于记忆,不管是否源地址段与目标地址段重复,我们从尾端进行拷贝。
dest = dest+size-1;
src = src+size-1;
while(size>0)
{
*dest-- = *src--;
size--;
}
}


memmove()函数分析:下面是以memmove()的例子来说明边界问题的考虑:memmove()实现的算法是将内存中某块固定大小的区域移动到另外一个区域。最常见的写法

·

void memmove(void* pDst,const void* pSrc, size_t size)
{
assert(pDst != NULL);
assert(pSrc != NULL);
assert(size >= 0);

char* pstrSrc=(char*)pSrc;
char* pstrDst=(char*)pDst;
while(size--)
*pstrDst++ = *pstrSrc++;
}
仔细观察上面程序,会发现有问题。两块内存区域之间由于位置关系可能会发生重叠。重叠通常有三种情况,一是目标区域的首地址落在源区域内;二是目标区域的尾地址落在源区域内;三是两个区域完全重叠。从结果上来看,只有第一种情况才有可能发生与预期不相同的结果,即复制的数据被后续复制数据覆盖的错误。为了解决这个问题,memmove()通常使用反方向复制的方法,从源区域的尾地址开始复制,这样就能避免出现已复制的数据被后续复制覆盖的错误。代码如下:

void memmove(void* pDst,const void* pStr, size_t size)
{
assert(NULL != pDst);
assert(NULL != pStr);
assert(size >= 0);
//上面的第一种情况,目标区域的首地址落在源区域内,从尾端开始复制
if((pSrc<pDst) && ((char*)pSrc+size > pDest))
{
char *pstrSrc = (char*)pSrc + size -1;
char *pstrDst = (char*)pDst + size -1;
while(size--)
*pstrDst-- = *pstrSrc--;
}
else
{
char* pstrSrc = (char*)pStr;
char* pstrDst = (char*)pDst;
while(size--)
*pstrDst++ = *pstrSrc++;
}
}

由于在内存拷贝时会出现目标区域的尾地址落在源区域内这一情况,所以前面在实现memcpy(),函数时不管怎么样都从尾地址开始拷贝,遇到这种情况时就会出问题,所以完整的分析见实现memmove()函数的分析。

void *memset(void *s, int ch, size_t n);将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值,
块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作, 其返回值为指向S的指针。:

void* Memset(void* buffer, char c, int count)
{
assert(NULL != buffer && count>=0);
//按字节赋值,所以将void*类型的指针转换为char*类型
char* pBuff = static_cast<char*> (buffer);
while(count--)
{
*pBuff++ = c;
}
return buffer;
}

strcmp()函数的实现:在C语言中int strcmp(char *str1,char * str2),它的功能是比较str1和str2,
当str1<str2,返回值<0
当str1>str2,返回值>0
当str1=str2,返回值=0

int strcmp(char *str1,char *str2)
{
while(*str1&& *str2 && *str1==*str2)
{
++str1;
++str2;
}

return *str1 - *str2;
}
在C语言里strstr()的原型为:char* *strstr(char *s1,char *s2),他的功能是从字符串s1中寻找s2第一次出现的位置(不比较结束符)返回第一次出现位置的指针。下面是代码:

/*未经过优化过的算法*/
const char* strstr1(const char* s1, const char* s2)
{
assert(s1 && s2);
const char *r = s2,*p;
const char *p1 = s1;
while('\0' != *p1)
{
p = p1;
r = s2;
while(*p == *r)
{
p++;
r++;
}
if('\0' == *r)
{
return p1;
}
else
{
p1++;
}
}
return NULL;
}

/*经过优化过后的算法,加入了很多条件判断*/
const char* strstr2(const char* str1,const char* str2)
{
assert(NULL != str1 && NULL != str2);
const char* ps1 = str1;
const char* ps2 = str2;

int len1 = strlen(str1);
int len2 = strlen(str2);
if((len1 == 0) || (len2 == 0)) //两个字符串中有空串则返回NULL
return NULL;
/*如果str2的长度大于str1的长度,那么在str1中不可能存在str2,直接返回NULL*/
if(len2 > len1)
return NULL;
while('\0' != *ps1)
{
while((*ps1 == *ps2) && ('\0' != *ps2))
{
ps1++;
ps2++;
}
if('\0' == *ps2)//str2是str1的字串,返回str2在str1中首次出现的位置
{
return str1;
}
else//从str1中的下一个字符开始重新比较,重置ps1和ps2
{
ps2 = str2;
str1++;
ps1 = str1;
len1--;
}
/*此时str1中剩余的字符串已经小于str2的长度,已无法继续查找的必要*/
if(len1<len2)
{
return NULL;
}
}
return NULL;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐