【C++】手撕memcpy基础函数

Posted by 卢小胖 on 2022-07-15
Estimated Reading Time 3 Minutes
Words 725 In Total
Viewed Times

手撕字符串函数,包括:

  • memcpy
  • memmove
  • strcpy
  • strcat
  • strcmp
  • strstr

memcpy

memcpy函数是c和c++使用的内存拷贝函数,
函数原型是:void memcpy(voiddest, const void *src, size_t n);
表示由src指向地址为起始地址的连续n个字节的数据复制到以dest指向地址为起始地址的空间内。与strcpy相比,memcpy并不是遇到’\0’就结束,而是一定会拷贝完n个字节。

注意事项:对于地址重叠的情况,上述函数的行为是未定义的,因此要考虑到此问题

要点:void*类型的指针不能运算,必须强转

void* memcpy(void *dst, const void *src, size_t size)
{
    if (dst == nullptr || src == nullptr)
        return nullptr;
    char *temp_dst = (char*) dst;
    char *temp_src = (char*) src;

    if (temp_dst > temp_src && temp_dst < temp_src+size)
    {
        //内存重叠,自后向前拷贝
        temp_dst = temp_dst+size-1;
        temp_src = temp_src+size-1;
        while (size--)
        {
            *temp_dst-- = *temp_src--;
        }
    }
    else
    {
        //正常拷贝,从前往后
        while (size--)
        {
            *temp_dst++ = *temp_src++;
        }
    }
    return (void*) dst;
}

strcpy

strcpy 只能拷贝字符串,它遇到’\0’就结束拷贝。

char * strcpy(char* dst, const char* src)
{
    if (dst == nullptr || src == nullptr)
        return nullptr;
    char* res = dst;
    while (*dst != '\0')
    {
        *dst++ = *src++;
    }
    return res;
}

strcat

把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。

char* strcat(char *dest,const char *src) {
    //1. 将目的字符串的起始位置先保存,最后要返回它的头指针
    //2. 先找到dest的结束位置,再把src拷⻉到dest中,记得在最后要加上'\0'
    char *ret = dest;
    assert(dest!= nullptr);
    assert(src!= nullptr);
    while(*dest!='\0')
        dest++;
    while(*src!='\0')
        *(dest++)=*(src++);
    *dest='\0';
    return ret;
}

strcmp

把 str1 所指向的字符串和 str2 所指向的字符串进⾏⽐较。

//该函数返回值如下:
//如果返回值 < 0,则表示 str1 ⼩于 str2。
//如果返回值 > 0,则表示 str1 ⼤于 str2。
//如果返回值 = 0,则表示 str1 等于 str2。
//'\0'的ascii码是0

int strcmp(const char* str1, const char*str2){
assert(str1 != nullptr && str2 != nullptr);
while (*str1 != '\0' && *str2 != '\0' && *str1 == *str2){
str1++;
str2++;
}

//可以用*str1 - *str2替代
if(*(unsigned char*)str1 < *(unsigned char*)str2){
return -1;
}
else if (*(unsigned char*)str1 > *(unsigned char*)str2){
return 1;
}
return 0;

//最简单的方式:*str1 - *str2
//return *str1 - *str2;
}

strlen

获取字符串长度,通过起始地址和’\0’地址间隔判断

int strlen(const char *str) {
	assert(str != NULL);
	int len = 0;
	while( (*str++) != '\0'){
        len++;
    }
	return len;
}

strfind

在字符串 str1中查找第⼀次出现字符串 str2 的位置,不包含终⽌符 ‘\0’。

char* strstr(const char *str1, const char *str2) {
     char* tmp_s = str1;
     assert(str1 != nullptr);
     assert(str2!= nullptr);
    //若str2为空,则直接返回空
     if(!str2){
         return nullptr;
     }
    //若不为空,则进⾏查询
     while(*tmp_s != '\0') {
     	char* s1 = tmp_s;
     	char* s2 = str2;
     	while(*s1 != '\0' && *s2!='\0' && *s1 == *s2){
            s1++;
            s2++;
        }
        //若s2先结束
        if(*s2 == '\0'){
			return str2;
        }
         //若s1先结束⽽s2还没结束,则返回空
     	if(*s2 != '\0' && *s1== '\0'){
             return nullptr;
        }
    	tmp_s++;
     }
     return nullptr;
}