首页 > 试题广场 >

实现一个Memcpy函数

[问答题]
void *memcpy(void *dst, const void *src, size_t len)
{
 if(NULL == dst || NULL == src){
  return NULL;
 }
 
 void *ret = dst;
 
 if(dst <= src || (char *)dst >= (char *)src + len){
  //没有内存重叠,从低地址开始复制
  while(len--){
   *(char *)dst = *(char *)src;
   dst = (char *)dst + 1;
   src = (char *)src + 1;
  }
 }else{
  //有内存重叠,从高地址开始复制
  src = (char *)src + len - 1;
  dst = (char *)dst + len - 1;
  while(len--){
   *(char *)dst = *(char *)src;
   dst = (char *)dst - 1;
   src = (char *)src - 1;
  }
 }
 return ret;
}

编辑于 2015-10-12 15:48:46 回复(28)
为什么是考虑内存重叠?
一共有两种复制方法,从前往后,从后往前。
应该是目标地址小于源地址就从前往后复制,
目标地址大于源地址就从后往前复制吧。
不知道我想得对不
发表于 2017-08-15 11:23:43 回复(3)
在linux中,memcpy是不考虑内存重叠的隐患问题的,即方法一;  memcpy的改进版,考虑上内存重叠问题,就是memmove,即方法二。
方法一:
void *memcpy(void *dest, const void *src, size_t n)
{
	char *tmp = (char *)dest;
	char *s = (char *)src;

	while (n--)
		*tmp++ = *s++;
	return dest;
}

方法二:
void *memmove(void *dest, const void *src, size_t n)
{
	char *tmp, *s;

	if (dest <= src) 					//没有内存重叠,从低地址开始复制
	{
		tmp = (char *) dest;
		s = (char *) src;
		while (n--)
			*tmp++ = *s++;
	}
	else 								 //有内存重叠,从高地址开始复制
	{
		tmp = (char *) dest + n;
		s = (char *) src + n;
		while (n--)
			*--tmp = *--s;
	}
	return dest;
}

发表于 2016-08-02 13:58:07 回复(0)
/*
**实现 memcpy
**2015.11.3
*/
#include <stdio.h>
#include <assert.h>
#include <string.h>

void *my_memcpy(void *dst,const void *src,size_t num)
{

	assert(dst != NULL && src != NULL);

	void *ret = dst; 
	int wordnum = num / sizeof(int);//按int字长拷贝
	int slice = num % sizeof(int);//按char字节拷贝

	//当src与dst相交,并且dst在右侧(低地址-->>高地址)
	if(dst >= src && (char *)dst <= (char *)src + num - 1)//内存重叠高地址判断
	{
		dst = (char *)dst + num;
		src = (char *)src + num;
		while(wordnum--)
			{
				//*(int *)dst-- = *(int *)src--;  //这样写实际只移动了一位
				dst = (int *)dst - 1;			  //应该先移位后复制
				src = (int *)src - 1;
				*(int *)dst = *(int *)src;		 
			}
		while(slice--)
			{
				dst = (char *)dst - 1;
				src = (char *)src - 1;
				*(char *)dst = *(char *)src;
			}
	}
	//处理正常情况,也可以处理当src与dst相交,并且dst在左侧(低地址-->>高地址)
	else
	{
		while(wordnum--)
			{
				*(int *)dst = *(int *)src;
				dst = (int *)dst + 1;
				src = (int *)src + 1;
			}
		while(slice--)
			*(char *)dst++ = *(char *)src++;
	}

	return ret;
}


int main()
{
	char a[10];
	char b[20] = {"hello"};		  //取20方便调试内存重叠
	// my_memcpy(a,b,strlen(b)+1);
	// printf("b = %s\n",b);
	// printf("a = %s\n",a);


	my_memcpy(b+2,b,strlen(b)+1);  //right
	printf("b = %s\n",b);
	printf("b+2 = %s\n",b+2);

	// printf("b+2 = %s\n",b+2);      //right
	// printf("b = %s\n",b);
	// my_memcpy(b,b+2,strlen(b+2)+1);
	// printf("b+2 = %s\n",b+2);
	// printf("b = %s\n",b);


	return 0;
}

编辑于 2015-11-03 20:29:13 回复(0)
//方法一:不考虑内存重叠
void* memcpy(void *dest, void *src, size_t size)
{
    if(dest == nullptr || src == nullptr)
        return nullptr;
    void *result = dest;
    
    while(size--)
    {
        *(char*)dest = *(char*)src;
        dest = (char*)dest+1;
        src = (char*)src+1;
    }
    
    return result;
}

//方法二:考虑内存重叠(类似于memmove)
void *memcpy(void *dest, void *src, size_t size)
{
    if(dest == nullptr || src == nullptr)
        return nullptr;
    void *result = dest;

    if(dest < src || (char*)src+size < (char*)dest)//没有内存重叠
    {
        while(size--)
        {
            *(char*)dest = *(char*)src;
            dest = (char*)dest+1;
            src = (char*)src+1;
        }
    }
    else//有内存重叠
    {
        dest = (char*)dest+size-1;
        src = (char*)src+size-1;
        while(size--)
        {
            *(char*)dest = *(char*)src;
            dest = (char*)dest-1;
            src = (char*)src-1;
        }
    }
    return result;
}

发表于 2015-08-04 09:45:08 回复(1)
反对以上所有笔记作为答案,,不是考虑不全面就是注释不完整或者不正确,给出一个较为完整的总结,http://blog.csdn.net/u014492609/article/details/50878767,欢迎各位道友批评指正。

发表于 2016-03-13 18:11:55 回复(4)

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

用法:#include<string.h>

功能:从源src所指的内存地址的起始位置开始,拷贝n个字节的数据到目标dest所指的内存地址的起始位置中。

说明:

1srcdest所指内存区域不能重叠,函数返回指向dest的指针。如果srcdest以任何形式出现了重叠,它的结果是未定义的。

2)与strcpy相比,memcpy遇到’\0’不结束,而且一定会复制完n个字节。只要保证src开始有n字节的有效数据,dest开始有n

节内存空间就行。

3)如果目标数组本身已有数据,执行memcpy之后,将覆盖原有数据(最多覆盖n个)。

如果要追加数据,则每次执行memcpy()后,要将目标地址增加到要追加数据的地址。

4sourcedestin都不一定是数组,任意的可读写的空间均可。

实现memcpy库函数:

#include<iostream>

#include<stdio.h>

#include<string.h>

#include<stdlib.h>

using namespace std;

void * memcpy(void * dst,void * src, size_t s)

{

       const char * psrc = static_cast<const char *>(src);

       const char * pdst = static_cast<const char *>(dst);   //类型强制转换成char *

       if(psrc==NULL||pdst==NULL)

              return NULL;

       if(pdst>psrc && pdst<(psrc+s))         //指的是两个区间有重合

       {

              for(size_t i=s-1;i!=-1;i--)

                     pdst[i]=psrc[i];

       }

       else

       {

              for(size_t i=0;i<s;i++)

                     pdst[i]=psrc[i];

       }

       return dst;

      

}

 

int main()

{

       char buf[100]="abcdefghijk";

       memcpy(buf+2,buf,5);

       printf("%s\n",buf+2);

       return 0;

}

发表于 2018-03-09 10:30:04 回复(0)
我看几乎所有的答案都没有边界检查,传进去的size如果是src的字节数,那缓冲区不够大的情况下肯定不行的,如果size是dst的字节数,那如果dst的缓冲区比src的大,后面的一部分拷贝就是越界操作了,所以vs才对几乎所有的c运行时函数进行了改写,用加_s区分,就是为了做边界检查。
void *memcpy(void *memDst, size_t dstSize
    , const void *memSrc, size_t srcSize)
{
    assert(memDst != NULL && memSrc != NULL);

    int i = 0, min = dstSize < srcSize ? dstSize : srcSize;
    char *pDst = (char *)memDst;
    const char *pSrc = (char *)memSrc;
    if (pSrc < pDst && pDst < pSrc + srcSize)
    {
        i = --min;
        while (i >= 0)
        {
            pDst[i] = pSrc[i];
            i--;
        }
    }
    else
    {
        while (i < min)
        {
            pDst[i] = pSrc[i];
            i++;
        }
    }
    
    return memDst;
}

发表于 2017-09-19 13:42:58 回复(1)
1、考虑指针是否为NULL
2、考虑内存是否重叠问题
3、高效性,32总线可以每次复制一个int,64位总线可以每次复制一个long long
发表于 2016-09-04 19:34:19 回复(2)
黑盒视角。尽管用,有问题找我。我叫雷锋。 void* Memcpy( void* dst,const void* src,size_t size) { return memmove(dst,src,size); }
编辑于 2020-04-12 23:38:54 回复(0)
//
// Created by yudw on 2017/8/6.
//

#pragma once

#include <iostream>

#define debug_

namespace yudw
{
    // 注意当内存有重叠时,src部分会被覆盖

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

        char *pdst = static_cast<char*>(dst);
        const char *psrc = static_cast<const char*>(src);

        // 1.内存无重叠
        // [dst + 0, dst + size -1] , [src + 0, src + size -1], [dst + 0, dst + size -1]
        if(pdst + size <= src || pdst >= psrc + size)
        {
#ifdef debug_
            std::cout<<"no cover:";
#endif

            while(size--)
            {
                *pdst++ = *psrc++;
            }
        }
        else    // 2.内存有重叠
        {
            if(pdst < psrc)   // 2.1 dst 在左,有部分重叠,从前往后直接拷贝就行
            {
#ifdef debug_
                std::cout<<"left cover:";
#endif
                while(size--)
                {
                    *pdst++ = *psrc++;
                }
            }
            else    // 2.2 dst 在右侧, 有部分重叠,从后向前拷贝,src重复部分会被dst覆盖
            {
#ifdef debug_
                std::cout<<"right cover:";
#endif
                while(size--)
                {
                    *(pdst + size) = *(psrc + size);
                }
            }
        }

        return dst;
    }
}


编辑于 2017-08-07 10:44:20 回复(2)
这道题目的要点是:1、接口设计的通用性 2、考虑内存重叠的情况
void mymemcpy(void *dst, const void *src, size_t num)
{
 assert((dst != NULL) && (src != NULL));
 const char *psrc = (const char*) src;
 char* pdst = (char*)dst;
 if (pdst > psrc && pest < psrc + num)
 {
  for (int i = num - 1;i != -1; --i)
  {
   pdst[i] = psrc[i];
  }
 }
 else
 {
  for (size_t i = 0;i < num;i++)
  {
   pdst[i] = psrc[i];
  }
 }
}

编辑于 2015-05-18 13:03:18 回复(1)
不知道
发表于 2016-03-20 01:22:56 回复(0)
<p>文本你让我写个屁</p>
发表于 2021-01-07 18:49:42 回复(0)
<p>啊</p>
发表于 2020-11-28 16:36:11 回复(0)
<p>佛坪县</p>
发表于 2020-11-28 15:02:07 回复(0)
<p>学习</p>
发表于 2020-09-07 16:44:04 回复(0)
<p>test</p><p><br></p>
发表于 2020-06-12 11:25:46 回复(0)
传入目的地址指针,源地址指针,返回成功失败
发表于 2020-05-02 12:26:54 回复(0)
void *memcpy(void *dst, const void *src, size_t len) {  if(NULL == dst || NULL == src){   return NULL;  }    void *ret = dst;    if(dst <= src || (char *)dst >= (char *)src + len){   //没有内存重叠,从低地址开始复制   while(len--){    *(char *)dst = *(char *)src;    dst = (char *)dst + 1;    src = (char *)src + 1;   }
发表于 2020-04-24 00:22:38 回复(0)