strcpy()、memcpy()、memmove()源码、源码优化及异同

一、strcpy(char* dest, const char* src),只能拷贝字符串数据类型。

源代码或优化代码:

//strcpy
char* Strcpy(char* dest, const char* src)
{
    char * address = dest;   //需要这句,因为直接返回dest,地址不是首地址
    assert((src != NULL) && (dest != NULL));
    while ((*dest++ = *src++) != '\0');
    return address;
}   //10分

void main_char_strcpy() 
{
    //char * s1 = NULL;
    //char * s2 = NULL;  //引发断言
    char s1[10];
    char s2[] = "huang";
    Strcpy(s1, s2);
    cout << s1 << endl;
    system("pause");
}
void main_string_strcpy()
{
    string s1;
    string s2("huang");
    s1.resize(10);
    //Strcpy(s1, s2);   //不能是string ,只适用于字符串
    system("pause");
}

二、memcpy(void * dest,const void  *src,size_t count),任何数据类型,不对是否存在重叠区域进行判断。 如果两数组重叠,不做该函数的行为。 memcpy函数假设要复制的内存区域不存在重叠,所以你要确保你进行复制操作的的内存区域没有任何重叠

源代码或优化代码:

//memcpy
void* Memcpy(void * dest,const void  *src,size_t count)
{
    assert((dest != NULL) && (src != NULL));
    char * tag_dest = (char * )dest;   //需要这一步,直接返回dest,返回的地址不是头
    char * tag_src = (char *)src;
    assert((tag_dest < tag_src) || (tag_dest >= tag_src + count)); //断言内存不会重叠
    while (count--)    //不对是否存在重叠区域进行判断
    {
        *tag_dest++ = *tag_src++;
    }
    return dest;  //返回原地址
}
void main_Memcpy()
{
    string s1[6];
    string s2[] = { "huang", "xian", "gao", "su", "xiao", "lu" };
    Memcpy(s1, s2, sizeof(s2));

    for(int i = 0; i < sizeof(s1) / sizeof(string);i++)
    {
        cout << s1[i] << endl;
    }
    system("pause");
}

三、memmove(void * dest,const void * src,size_t count),任何数据类型,对是否存在重叠区域进行判断,并作出处理。即memmove(),如果两函数重叠,赋值仍正确进行。 当不能保证内存空间是否有重叠时,并且源数组不再使用,为了确保复制的正确性,你必须用memmove。

函数作用:用于从src拷贝count个字节到dest,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域区域的字节拷贝到目标区域中,但复制后src内容会被更改,但是当目标区域与源区域没有重叠则和memcpy函数功能相同。

memmove可以被分为三种情况:

1.dest<src,源内存的首地址大于目标内存的首地址,进行正向拷贝,此时无需担心dest+count>src,超出的部分只会将src已经拷贝过的部分覆盖,不会造成影响

2.dest>=src+count,此时目标内存的首地址大于源内存的首地址+count,进行反向拷贝,由于两首地址相距足够大,无需担心被覆盖问题

3.dest<src+count,此时目标内存的首地址小于源内存的首地址+count,此时随着src向dest拷贝,src前段会将末端的覆盖掉,导致数据丢失,此时我们需要手动“扩容”,将两个指针同时增加count-1

strcpy()、memcpy()、memmove()源码、源码优化及异同

在上面按字节拷贝中考虑了拷贝覆盖,连续的一段空间存放数据是从低地址到高地址进行存放。先从源地址读出数据,然后写入到目的地址空间中。目的空间的起始地址如果在源数据空间之内就会出现内存覆盖的情况。

这种情况先从尾部拷贝,避免覆盖数据,不过这种情况也会破坏src空间数据,src前使用了const关键字,也就是空间只读,在函数内部不修改src空间数据。

而标准库的memcpy并没有将写覆盖认为是内存拷贝,而是内存移动。memcpysrc代表一块内存空间,并用const关键字修饰,并不希望内存块被破坏。

对于写覆盖(这里是内存块移动)标准库推荐使用memmove函数

源代码或优化代码:
//memmove (按字节拷贝)
void* Memmove_Byte(void * dest,const void * src,size_t count)
{
    char * pdest = (char *)dest;
    assert((dest != NULL) && (src != NULL));
    if ((dest < src) || (((char *)src + count) <= dest))
    {
        while (count--)
        {
            *(char *)dest = *(char *)src;
            dest =(char *)dest + 1;
            src = (char *)src + 1;
        }
    }
    else
    {
        dest = (char *)dest + count - 1;
        src = (char *)src + count - 1;
        while (count--)
        {
            *(char *)dest = *(char *)src;
            dest = (char *)dest - 1;
            src = (char *)src - 1;
        }
    }
    return pdest;  //返回首地址
}
//代码简化版,与上相同
void * Memmove_Byte_Small(void *dest, const void * src, size_t count)
{
    char * pdest = (char *)dest;
    char * psrc = (char *)src;
    assert((pdest != NULL) && (psrc != NULL));
    if ((pdest < psrc) || (pdest >= psrc + count))
    {
        while (count--)
        {
            *pdest++ = *psrc++;
        }
    }
    else
    {
        pdest = pdest + count - 1;
        psrc = psrc + count - 1;
        while (count--)
        {
            *pdest-- = *psrc--;
        }
    }
    return dest;  //返回首地址
}

//Memcpy优化(按4字节拷贝)
void * Memmove(void *dest, const void * src, size_t count)
{
    assert((dest != NULL) && (src != NULL));
    int c1 = count / 4;
    int c2 = count % 4;
    int *pdest = (int *)dest;
    int *psrc = (int *)src;
    char * char_dest = NULL;
    char * char_src = NULL; 
    if ((pdest < psrc) || (pdest >= psrc + count))
    {
        while (c1--)
        {
            *pdest++ = *psrc++;
        }
        char_dest = (char *)pdest;
        char_src = (char *)psrc;
        while (c2--)
        {
            *char_dest++ = *char_src++;
        }
    }
    else
    {

        char_dest = (char *)pdest + count - 1;
        char_src = (char *)psrc + count - 1;
        while (c2--)
        {
            *char_dest-- = *char_src--;
        }
        pdest = (int *)char_dest;
        psrc = (int *)char_src;
        while (c1--)
        {
            *pdest-- = *psrc--;
        }
    }
    return dest;
}


void main()
{
    string s1[6];
    string s2[] = { "huang", "xian", "gao", "su", "xiao", "lu" };
    Memmove(s1, s2, sizeof(s2));
    for (int i = 0; i < sizeof(s1) / sizeof(string); i++)
    {
        cout << s1[i] << endl;
    }
    system("pause");
}

四、上述中,存在一个问题,源指针所指向的内容被修改,那么拷贝之后,我还需要使用源指针的内容,那不就会出现错误了吗?src前使用了const关键字,也就是空间只读,在函数内部不修改src空间数据,但是内存空间还是修改了数据。这个不就给后来使用src内容造成错误了吗?不会是规定以后不准再用了吧(阔怕)

如走过路过的大佬知道,请告知

参考:https://www.cnblogs.com/chuanfengzhang/p/8447251.html

          https://blog.****.net/enjoymyselflzz/article/details/81175869