memcpy函数的实现及内存重叠问题的分析

时间:2022-01-26 07:23:17

函数原型
void *memcpy(char *dest,const char *src,size_t n)

头文件
#include<string.h>或#include<memory.h>
参数
1.memcpy中的三个参数分别为目标字符串 char *dest。

2.源字符串 const char *src const 这里const是对src所指向的静态常量区的字符串常量进行修饰,保护源字符串在内存拷贝的过程不被修改。

3.需要拷贝的字节个数 size_t n
typedef unsigned int szie_t 这里的size_t其实就是为无符号整型unsigned int进行别名,因为在内存拷贝函数中,一方面拷贝的字节数不可能是负整数,另一方面字节数不可能是实数,出于上面两个方面的考虑字节数的类型应定义为size_t.

返回值
memcpy returns the value of dest. 返回目标字符串的地址

函数描述
The memcpy function copies count bytes of src to dest. If the source and destination overlap, this function does not ensure that the original source bytes in the overlapping region are copied before being overwritten. Use memmove to handle overlapping regions.
memcpy函数将src的字节数复制到dest。如果源和目标重叠,这个函数不能确保重叠区域的原始源字节在被覆盖之前被复制。
这里已经提到了内存覆盖的问题,而在C语言却并没有对这种现象做相关的规定或检查,也就是说对于这种现象C语言是缺省。后边会详细分析如何处理在字符串拷贝函数中内存重叠的问题。

内存重叠
memcpy函数的实现及内存重叠问题的分析
注意:在这里的内存重叠我们只考虑为了成功实现内存拷贝要排除的内存重叠的情况。
当然也可能出现目标字符串覆盖源字符串的情况,但如果其满足成功拷贝的条件即可。

可以把src、dest、src+n比作数轴上的三个数字,当进行内存拷贝是。如果dest处于src和src+n之间时,一定会出现内存覆盖的现象,而且还会改变源字符串的内容,进行错误的拷贝。因此为了能够合理进行拷贝,提出如下的解决方案。
一. 高地址向低地址进行拷贝
由于在虚拟地址空间中,栈空间的生长方向是高地址向低地址生长,首先采用这种方式。简略的讲就是源字符串中的字符从前往后向目标字符串按给定字节的大小依此进行拷贝。
观察上图,可以得到两个合理的区间即不会出现内存覆盖的区间。
(1)dest<=src

第一种情况dest=src,此时源字符串与目标字符串指针指向同一个位置,拷贝的过程相当自己给自己赋值,因此拷贝结束 后源字符串并没有发生变化。

第二种情况dest < src,这样的拷贝尽管会覆盖src的内容,出现了内存重叠,但其可以完成内存拷贝的功能,并没有将错误的信息拷贝过来。

(2)dest>=src+n
memcpy函数的实现及内存重叠问题的分析
由上图可见,当dest>=src+n,无论如何都不会出现内存重叠的问题。

二. 低地址向高地址拷贝
这种拷贝方式是为了处理,dest处于src和src+n之间,即一定会出现内存重叠的问题。
为了避免出现这种情况,我们可以将src和dest都移动 n-1个位置,这样我们就可以从地址值向高地址进行拷贝,这样尽管也有可能目标字符串覆盖源字符串的情况,但是定影可以得到一个正确的拷贝。
大家可以自行进行验证!

memcpy函数的实现

#include<stdio.h>
#include<assert.h>
#include <stddef.h>


void *my_memcpy(char *dest,const char *src,size_t n)
{
    assert(NULL != dest);
    assert(NULL != src);
    char *res = (char*)dest;


    char *p = (char*)dest;
    const char *q = (const char*)src;
    if(p <= q || p >= q+n)
    {
        while(n--)
        {
            *p++ = *q++;
        }
    }
    else
    {
        p = p+n-1;
        q = q+n-1;
        while(n--)
        {
            *p-- = *q--;
        }
    }
    return res;
}

来源于网上的测试用例

void Test()
{
    char p1[256] = "hello,world!";
    char p2[256] = {0};
    MyMemMove(p2,p1,strlen(p1)+1);
    printf("%s\n",p2);
    MyMemMove(NULL,p1,strlen(p1)+1);
    MyMemMove(p2,NULL,strlen(p1)+1);
    MyMemMove(p1+1,p1,strlen(p1)+1);
    printf("%s\n",p1);
    MyMemMove(p1,p1+1,strlen(p1)+1);
    printf("%s\n",p1);
}