动手实现自己的memcpy和strcpy

目录

一、问题由来

二、动手实现你的memcpy

1.单字节拷贝

2.四字节拷贝(提升效率)

三、动手实现你的strcpy

四、测试结果


一、问题由来

        C库中已提供有原生的memcpy、memmove以及strcpy等函数,位于<string.h>头文件中。

void *memcpy(void *dst, const void *src, size_t n);

        上面是C库中原生的memcpy声明,该memcpy在dst与src两者的内存地址不互相重叠时,可以得到正确的处理结果,而两者的内存地址有重叠时,该接口并不保证处理结果的正确性。

        接着则是memmove在string.h中的声明,memmove比memcpy更先进的原因则在于:它解决了内存重叠时的拷贝情形。但memmove在不同编译器上的实现会有所不同,故memmove在内存不重叠时,不一定会比memcpy更快

void *memmove(void *dst, const void *src, size_t count)

       再观之strcpy的函数原型,发现其声明与memcpy有几分相似,从中也可以看出:memcpy和memmove比strcpy的适用范围更大,strcpy只是它们的特殊情形

char *strcpy(char *dest, const char *src)

        说到此,不论是探索的需求,还是面试的需求,想必读者已经迫不及待地想要手撕一遍memcpy和strcpy的实现了,here we go! (但先说好了,咱们只是原理上的实现,而不是探索极致的效率,真正的源码情况会考虑到更多的情况。)

二、动手实现你的memcpy

1.单字节拷贝

        src与dest两者的相对位置存在好几种可能性,有些相对位置不会影响到拷贝的结果,而有些则会影响到拷贝的结果。

情形1:src + size < dest,即待拷贝区域与dest区域不重叠

 这种情形下,只需将src中的数据从前往后挨个地拷贝到dest中即可。

情形2:dest与src的区域有重叠,但dest < src

        这种情形下,虽然存在重叠的内存区域,但依然可以采取从前往后挨个拷贝的策略,并不会影响拷贝的结果。

情形3:dest与src的区域有重叠,且src < dest

         这种情形下,若依然采取从前往后的拷贝策略,会发现src[0]拷贝至dest[0]时已将src[4]数据给污染了,往后拷贝到dest[4]时自然不会是正确结果。从前往后拷贝行不通,将src中的内容从后往前地拷贝至dest中却是可行的。

(代码实现)

//-------------------------按单字节拷贝(基础实现)-------------------//
void* my_memcpy_byte(void* dest, const void* src, size_t n)
{
    if(dest == NULL || src == NULL) return NULL;
  
    char *pdst = (char*)dest;
    const char *psrc = (char*)src; 
    
    //情形1与情形2
    if( pdst > (psrc + n) || pdst < psrc) 
    {  
        printf("no-overlap: \n");
        while(n--) *pdst++ = *psrc++;  //顺序copy
    }
    
    //情形3
    else 
    {  
        printf("overlap: \n");
        pdst = dest + n - 1;  //定位到前n个字节的末尾
        psrc = src + n - 1;

        while(n--) *pdst-- = *psrc--;  //逆序copy
    }

    return dest;
}

2.四字节拷贝(提升效率)

单字节拷贝只是基础性的实现,其效率还有进一步优化的空间,为此可以考虑按多个字节来拷贝:

//----------------------按4字节拷贝(提升效率)-------------------//
void* my_memcpy(void* dest, const void* src, size_t n)
{
    if(dest == NULL || src == NULL) return NULL;

    int *pdst = (int*)dest;
    int *psrc = (int*)src;
    char *byte1, *byte2;
    int cnt1 = n / 4; //多少个4字节
    int cnt2 = n % 4; //余出多少个单字节

    //pdst > psrc + n 这样判断会有问题,虽然按4字节进行拷贝,但内存重叠情况还是按单个字节来比较
    if( (char*)pdst > ((char*)psrc + n) || pdst < psrc) 
    {  
        printf("no-overlap: \n");

        while(cnt1--) *pdst++ = *psrc++;  //4字节拷贝
        
        byte1 = (char*)pdst;
        byte2 = (char*)psrc;
        while(cnt2--) *byte1++ = *byte2++;  //单字节拷贝
    }
    else 
    {
        printf("overlap: \n");

        byte1 = (char*)pdst + n - 1;
        byte2 = (char*)psrc + n - 1;
        while(cnt2--) *byte1-- = *byte2--;  //单字节拷贝

        byte1++, byte2++;
        pdst = (int*)byte1 - 1;
        psrc = (int*)byte2 - 1;
        while(cnt1--) *pdst-- = *psrc--;   //4字节拷贝
    }

    return dest;
}

三、动手实现你的strcpy

char* my_strcpy(char* dest, const char* src)
{
    if(dest == NULL || src == NULL) return NULL;

    char *psrc = (char*)src;
    int n = 0;
    while(*psrc)  //得到长度
    {  
        n++;
        psrc++;
    }

    my_memcpy_byte(dest, src, n);

    return dest;
}

四、测试结果

附上整体代码,以供读者参考。本例完全脱离于<string.h>而实现,可放心食用。

#include<stdio.h>

//-------------------------按单字节拷贝(基础实现)-------------------//
void* my_memcpy_byte(void* dest, const void* src, size_t n)
{
    if(dest == NULL || src == NULL) return NULL;
  
    char *pdst = dest;
    const char *psrc = src; 

    if( pdst > (psrc + n) || pdst < psrc) //不重叠 || 虽重叠但dst < src
    {  
        printf("no-overlap: \n");
        while(n--) *pdst++ = *psrc++;  //顺序copy
    }
    else //重叠且src < dest
    {  
        printf("overlap: \n");
        pdst = dest + n - 1;
        psrc = src + n - 1;

        while(n--) *pdst-- = *psrc--;  //逆序copy
    }

    return dest;
}

//----------------------按4字节拷贝(提升效率)-------------------//
void* my_memcpy(void* dest, const void* src, size_t n)
{
    if(dest == NULL || src == NULL) return NULL;

    int *pdst = (int*)dest;
    int *psrc = (int*)src;
    char *byte1, *byte2;
    int cnt1 = n / 4; //多少个4字节
    int cnt2 = n % 4; //余出多少个单字节

    //pdst > psrc + n 这样判断有问题,虽然按4字节进行拷贝,但内存重叠情况还是按单个字节来描述
    if( (char*)pdst > ((char*)psrc + n) || pdst < psrc) 
    {  
        printf("no-overlap: \n");

        while(cnt1--) *pdst++ = *psrc++;  //4字节拷贝
        
        byte1 = (char*)pdst;
        byte2 = (char*)psrc;
        while(cnt2--) *byte1++ = *byte2++;  //单字节拷贝
    }
    else 
    {
        printf("overlap: \n");

        byte1 = (char*)pdst + n - 1;
        byte2 = (char*)psrc + n - 1;
        while(cnt2--) *byte1-- = *byte2--;  //单字节拷贝

        byte1++, byte2++;
        pdst = (int*)byte1 - 1;
        psrc = (int*)byte2 - 1;
        while(cnt1--) *pdst-- = *psrc--;   //4字节拷贝
    }

    return dest;
}

char* my_strcpy(char* dest, const char* src)
{
    if(dest == NULL || src == NULL) return NULL;

    char *psrc = (char*)src;
    int n = 0;
    while(*psrc) //得到长度
    {  
        n++;
        psrc++;
    }

    my_memcpy_byte(dest, src, n);

    return dest;
}

int main()
{
    char a[] = "12345678900000000000000000";
    char b[] = "12345678900000000000000000";
    char buf[100] = {'0'};
    char *p = a + 3;
    char *p1 = b + 10;

    printf("%s\n", my_memcpy(p, a, 7));  //overlap: p所指位于a[0]~a[6]的范围内
    printf("%s\n", my_memcpy(p1, b, 7)); //p1所指不在b[0]~b[6]的范围内, 即无重叠
    printf("%s\n", my_strcpy(buf, a));  //独立的空间, 无重叠

    return 0;
}

[程序结果及分析]

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值