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

来源:互联网 发布:北京网络监控安装 编辑:程序博客网 时间:2024/06/04 17:50

在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)) //两个字符串中有空串则返回NULLreturn 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;}



原创粉丝点击