您的位置:首页 > 编程语言 > C语言/C++

C/C++之Memcpy and memmove

2015-08-13 10:01 309 查看
memcpy与memmove的目的都是将N个字节的源内存地址的内容拷贝到目标内存地址中。

但当源内存和目标内存存在重叠时,memcpy会出现错误,而memmove能正确地实施拷贝,但这也增加了一点点开销。

memmove的处理措施:

(1)当源内存的首地址等于目标内存的首地址时,不进行任何拷贝

(2)当源内存的首地址大于目标内存的首地址时,实行正向拷贝

(3)当源内存的首地址小于目标内存的首地址时,实行反向拷贝

-- memcpy实现

1
void
*
memcpy
(
void
*dest, 
const
void
*src, 
size_t
n)
2
{
3
char
*d = (
char
*) dest;
4
const
char
*s = (
const
char
*) src;
5
while
(n-–)
6
*d++ = *s++;
7
return
dest;
8
}
 

Notes:

memcpy的参数指针类型是void*,具体赋值操作是以字节为单位。

必须进行类型转换。

返回的还是void*型的dest。

-- memmove实现

01
void
*
memmove
(
void
*dest, 
const
void
*src, 
size_t
n)
02
{
03
char
*d = (
char
*) dest;
04
const
char
*s = (
const
char
*) src;
05
06
if
(s>d)
07
{
08
// start at beginning of s
09
while
(n--)
10
*d++ = *s++;
11
}
12
else
if
(s<d)
13
{
14
// start at end of s
15
d = d+n-1;
16
s = s+n-1;
17
18
while
(n--)
19
*d-- = *s--;
20
}
21
return
dest;
22
}
 示意图:

(1)内存低端 <-----s-----> <-----d-----> 内存高端 start at end of s
(2)内存低端 <-----s--<==>--d----->      内存高端 start at end of s
(3)内存低端 <-----sd----->              内存高端 do nothing
(4)内存低端 <-----d--<==>--s----->      内存高端 start at beginning of s
(5)内存低端 <-----d-----> <-----s-----> 内存高端 start at beginning of s

Notes:


s==d时,什么都不做。

d在前,正向拷贝。

d在后,逆向拷贝。

1.memmove

函数原型:void *memmove(void *dest, const void *source, size_t count)

返回值说明:返回指向dest的void *指针

参数说明:dest,source分别为目标串和源串的首地址。count为要移动的字符的个数

函数说明:memmove用于从source拷贝count个字符到dest,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中。

2.memcpy

函数原型:void *memcpy(void *dest, const void *source, size_t count);

返回值说明:返回指向dest的void *指针

函数说明:memcpy功能和memmove相同,但是memcpy中dest和source中的区域不能重叠,否则会出现未知结果。

原型:extern char *strcpy(char *dest,char *src);  功能:把src所指由NULL结束的字符串复制到dest所指的数组中。  说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串,返回指向dest的指针。


  其实在strcpy的实现比较多,但思想是一致的,一般用C来实现,但是memcpy和memmove这样的函数可能是用汇编实现的,并且充分利用块拷贝的思想,不会单字节单字节的拷贝。所以效率strcpy<memcpy.

memmove一般由于要判断内存是否重合,效率也会较memcpy低些。

1 /***
2 *@brief 以字节的方式直接拷贝
3 *库中实现是以汇编实现,
4 *其实可以直接调用strncat函数
5 ***/
6 void *memcpy(void *dst,void *src,size_t n)
7 {
8 char *dp = (char *)dst;
9 char *sp = (char *)src;
10 assert((src!=0)&&(dst!=0)&&(n>0));//not null
11 while(n--)
12 *(dp++) = *(sp++);
13 /**!边界*/
14 dp = '\0';
15 return dst;
16 }


memmove

1 void *memmove(void *dst,const void *src,int n)
2 {
3     char *dp = (char *)dst;
4     char *sp = (char *)src;
5     assert((src!=0)&&(dst!=0)&&(n>0));//not null
6     //非重叠
7     //dp < sp
8     //dp > (sp+n)
9     if(dp<sp||(sp+n)>=dp)
10     {
11         while(n--)
12             *(dp++) = *(sp++);
13         *dp = '\0';
14     }else if(sp<dp)//重叠 (此时条件 sp<dp<(sp+n))如果sp==dp则快速的返回
15     {//反向拷贝
16         sp += n;
17         dp += n;
18         *dp = '\0';
19         while(n--)
20             *(--dp) = *(--sp);
21     }
22     return dst;
23 }


 注意对于重合的要反向拷贝

/*
*Magicman
*myMemcpy.c
*不调用库函数,实现内存拷贝
*/

#include <stdio.h>

#include <assert.h>

#include <stdlib.h>

void *myMemcpy(void *dest, const void *src, int len)

{

assert((dest != NULL) && (src != NULL) && (len >= 0));


if (dest == src)

{

return dest;

}


while (len-- > 0)

{

*(char *)dest++ = *(char *)src++;

}


return dest;

}


int main(int argc, char argv[])

{

char str[20] = "Testing myMemory!";

char pstr[20] = "";

char *pp = str;

int ia[10] = {1,2,3,4,5,6,7,8,9,10};

int ib[10] = {};

int *ip = NULL;

myMemcpy((void *)pstr, (void *)str, sizeof(str));

printf("%s/n", pstr);

printf("%s/n", myMemcpy((void *)pp, (void *)str, 20));

myMemcpy((void *)ib, (void *)ia, 5*sizeof(int));

for (ip = ib; ip < ib + 10; ip++)

{

printf("%d ", *ip);

}

printf("/n");

return 0;

}

让自己实现memcpy库函数,要求考虑特殊情况,两段内存存在覆盖,以及指针为空的情况。

几点结论:
1,memcpy实现从源source中拷贝n个字节到目标destin中,src源数据应该保留。
2,memmove实现移动一块字节,src源数据可以不保留。
3,memcpy没有考虑内存覆盖问题(由assert条件可知);而memmove考虑了内存覆盖问题,并给出了解决办法。
4,memcpy和memmove中不需要考虑数组越界问题,dst的长度应该大于src的长度,这是调用者应该考虑的问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: