C常用字符串模拟、内存重叠问题

C常用字符串函数模拟、内存重叠问题

当我们在模拟字符串函数的时候需要注意两点:
检查参数合法性
对指针进行参数保护


字符串长度strlen

注意点:

  • 字符串以 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )。
  • 参数指向的字符串必须要以 ‘\0’ 结束。
  • 注意函数的返回值为size_t,是无符号的(易错)。
//注意返回值是size_t
size_t my_strlen(const char *string) {
    
    
    assert(string!=NULL);   	//1.检查参数合法性
    const char* src = string;	//2.对指针进行参数保护
    size_t len = 0;				//长度需要和返回值对应
    while (*src++ != '\0') {
    
    
        len++;
    }
    return len;
}

长度不受限制的字符串函数

strcpy

注意点:

  • 源字符串必须以 ‘\0’ 结束。
  • 会将源字符串中的 ‘\0’ 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。
char* my_strcpy(char* strDest, const char* strSrc)
{
    
    
    assert(strDest != NULL && strSrc != NULL); //1检查参数合法性
    char* pDest = strDest;
    const char* pSrc = strSrc; //2 保护参数
   
    while (*pDest++ = *pSrc++);  //先将*pSrc的值赋给*pDest,然后判断pSrc的值是不是'\0'然后pSrc++,pDest++;
    
    //当然也可以这样写
    /*
	while(*pSrc!='\0'){
		*pDest=*pSrc;
		pDest++;
		pSrc++;
	}
	*pDest='\0';
	*/
    return strDest;
}

strcat

提示:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。

char* my_strcat(char* strDest, const char* strSrc)
{
    
    
    
    assert(strDest!=NULL&&strSrc!=NULL);//1.检查参数的合法性
    char* pDest = strDest;  //2.对参数指针进行保护
    const char* pSrc = strSrc;
    
    //先找到strSrc的'\0'的位置
    while (*pDest !='\0') {
    
    
        pDest++;
    }
    //实行连接拷贝
    while (*pSrc!='\0') {
    
    
        *pDest++ = *pSrc++;
    }
    //增加结束标记
    *pDest = '\0';
    return strDest;
}

strcmp

//string1>string2 返回值大于0
//string1=string2 返回值等于0
//string1<string2 返回值小于0

int my_strcmp(const char* string1, const char* string2)
{
    
    
    assert(string1!=NULL&&string2!=NULL);   //1.参数合法性检查
    const char* ps1 = string1;              //2.指针进行参数保护
    const char* ps2 = string2;
    
    while (*ps1!='\0'||*ps2!='\0') {
    
        //当存在两个字符串没有同时到达末尾就循环 当同时到达末尾时,res=0即字符串相等
        if (*ps1 - *ps2 != 0)   //当存在两个字符不相等时就跳出循环,即使有一个字符串达到字符串末尾。我们在(*ps1-*ps2)进行统一判断。
            break;
        ps1++;
        ps2++;
    }
    int res = *ps1 - *ps2;  
    return res;
}


长度受限制的字符串函数

函数原型通过添加count值来限制所能操作字符串的长度。

strncpy

char* my_strncpy(char* strDest, const char* strSrc,size_t count)
{
    
    
    assert(strDest != NULL && strSrc != NULL); //1检查参数
    char* pDest = strDest;
    const char* pSrc = strSrc; //2 保护参数
    while (count--) {
    
    
        *pDest++ = *pSrc++;
    }
    return strDest;
}

strncat

说 明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。

char* my_strncat(char* strDest, const char* strSrc, size_t count)
{
    
       
    assert(strDest!=NULL&&strSrc!=NULL);
    char* pDest = strDest;
    const char* pSrc = strSrc;
    while (*pDest !='\0') {
    
    
        pDest++;
    }

    while (count--) {
    
    
        *pDest++ = *pSrc++;
    }
    *pDest = '\0';
    return strDest;

}

strncmp


int my_strncmp(const char* string1, const char* string2,size_t count)
{
    
    
    assert(string1 != NULL && string2 != NULL);   //1.参数合法性检查
    const char* ps1 = string1;              //2.指针进行参数保护
    const char* ps2 = string2;
    while (--count) {
    
             //要先减了再判断是不是>0,因为ps1++,ps2++会提前到达下一个字符
        if (*ps1 - *ps2 != 0)   //当存在两个字符不相等时就跳出循环,即使有一个字符串达到字符串末尾。我们在(*ps1-*ps2)进行统一判断。
            break;
        ps1++;
        ps2++;
    }
    int res = *ps1 - *ps2;
    return res;
}

内存操作函数

就是说这个标准规定memcpy是不处理的。但是基于编译器 可能会处理也可能不会处理。但是对于memmove这个标准规定是一定会处理的

memcpy

函数memcpy从src的位置开始向后复制num个字节的数据到des的内存位置。以字节为单位进行复制。
这个函数在遇到 ‘\0’ 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。

void* my_memcpy(void* dest, const void* src, size_t count) {
    
    
    assert(dest&&src);	//检查参数合法性
    char* pdest = (char *)dest;//指针进行参数保护
    const char* psrc = (const char*)src;
    while (count--) {
    
    
    	*pdest++ = *psrc++;
    }
    return dest;
}

memmove

和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理。

内存重叠:拷贝的目的地址在源地址范围内。所谓内存重叠就是拷贝的目的地址和源地址有重叠。

原型:void* my_memmove(void* dest, const void* src, size_t count);


在这里插入图片描述


解决办法:

判断是否内存重叠,内存重叠从后往前复制。
没有内存重叠,从前往后复制。

void* my_memmove(void* dest, const void* src, size_t count) {
    
    
    assert(dest&&src);
    char* pdest = (char *)dest;
    const char* psrc = (const char*)src;
    if (pdest>psrc&&pdest<psrc+count) {
    
     //判断有内存重叠
        pdest = pdest + count - 1;      //从后往前赋值
        psrc = psrc + count - 1;
        while (count--) {
    
       
            *pdest-- = *psrc--;     
        }
    }
    else {
    
    			//没有内存重叠的情况
        while (count--) {
    
    
            *pdest++ = *psrc++;
        }
    }
    return dest;
}

memmove和memcpy的唯一区别:在C语言标准中规定当内存发生局部重叠的时候,memmove保证拷贝的结果是正确的,memcpy不保证拷贝的结果的正确。memcpy只是不保证结果的正确性,但是结果也有可能是正确的,这取决于自己的编译器。有一些编译器memcpy函数也是处理了内存局部重叠的情况。

测试情况:
在这里插入图片描述

memcmp

int my_memcmp(const void* buf1, const void* buf2, size_t count)
{
    
    
    assert(buf1&&buf2);
    const char* pbuf1 = (const char*)buf1;
    const char* pbuf2 = (const char*)buf2;

    while (count--) {
    
    
        if (*pbuf1 - *pbuf2 != 0)
            break;
        pbuf1++;
        pbuf2++;
    }
    return (*pbuf1-*pbuf2);
}

memset

void* my_memset(void* dest, int c, size_t count)
{
    
    
    assert(dest);
    char* pdest = (char*)dest;

    while (count--) {
    
    
        *pdest++ = c;
    }
    return dest;
}

猜你喜欢

转载自blog.csdn.net/weixin_44627813/article/details/110232130