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;
}
在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;
}
/*
**实现 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;
}
//方法一:不考虑内存重叠
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;
}
函数原型:void *memcpy(void*dest, const void *src, size_t n);
用法:#include<string.h>
功能:从源src所指的内存地址的起始位置开始,拷贝n个字节的数据到目标dest所指的内存地址的起始位置中。
说明:
1)src和dest所指内存区域不能重叠,函数返回指向dest的指针。如果src和dest以任何形式出现了重叠,它的结果是未定义的。
2)与strcpy相比,memcpy遇到’\0’不结束,而且一定会复制完n个字节。只要保证src开始有n字节的有效数据,dest开始有n字
节内存空间就行。
3)如果目标数组本身已有数据,执行memcpy之后,将覆盖原有数据(最多覆盖n个)。
如果要追加数据,则每次执行memcpy()后,要将目标地址增加到要追加数据的地址。
4)source和destin都不一定是数组,任意的可读写的空间均可。
实现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;
}
//
// 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;
}
}
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];
}
}
}