c语言memcpy和memmove
memcpy
void *memcpy(void *str1, const void *str2, size_t n)
从存储区 str2 复制 n 个字节到存储区 str1
- str1 – 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
- str2 – 指向要复制的数据源,类型强制转换为 void* 指针。
- n – 要被复制的字节数。
memcpy的实现
#include <stddef.h> /* size_t */
void *memcpy(void *dest, const void *src, size_t n)
{
char *dp = dest;
const char *sp = src;
while (n--)
*dp++ = *sp++;
return dest;
}
例子1
// 将字符串复制到数组 dest 中
#include <stdio.h>
#include <string.h>
int main ()
{
const char src[50] = "http://www.runoob.com";
char dest[50];
memcpy(dest, src, strlen(src)+1);
printf("dest = %s\n", dest);
return(0);
}
运行结果
dest = http://www.runoob.com
思考,为什么要加1。
例子2
将 s 中第 11 个字符开始的 6个连续字符复制到 d 中:
#include <stdio.h>
#include<string.h>
int main()
{
char *s="http://www.runoob.com";
char d[20];
memcpy(d, s+11, 6);// 从第 11 个字符(r)开始复制,连续复制 6 个字符(runoob)
// 或者 memcpy(d, s+11*sizeof(char), 6*sizeof(char));
d[6]='\0';
printf("%s", d);
return 0;
}
运行
runoob
例子3 ——覆盖原有部分数据:
#include<stdio.h>
#include<string.h>
int main(void)
{
char src[] = "***";
char dest[] = "abcdefg";
printf("使用 memcpy 前: %s\n", dest);
memcpy(dest, src, strlen(src));
printf("使用 memcpy 后: %s\n", dest);
return 0;
}
运行
使用 memcpy 前: abcdefg
使用 memcpy 后: ***defg
可以看到abc这三个字符被覆盖。
memmove
从 str2 复制 n 个字符到 str1,但是在重叠内存块这方面,memmove()
是比 memcpy()
更安全的方法。如果目标区域和源区域有重叠的话,memmove()
能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后源区域的内容会被更改。如果目标区域与源区域没有重叠,则和 memcpy()
函数功能相同。
void *memmove(void *str1, const void *str2, size_t n)
- str1 – 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
- str2 – 指向要复制的数据源,类型强制转换为 void* 指针。
- n – 要被复制的字节数。
例子1
#include <stdio.h>
#include <string.h>
int main ()
{
const char dest[] = "oldstring";
const char src[] = "newstring";
printf("Before memmove dest = %s, src = %s\n", dest, src);
memmove(dest, src, 9);
printf("After memmove dest = %s, src = %s\n", dest, src);
return(0);
}
运行
Before memmove dest = oldstring, src = newstring
After memmove dest = newstring, src = newstring
memmove的实现
#include <iostream>
#include <string.h>
using namespace std;
//实现memmove库函数
void* memmove(void *dst, const void *src, size_t count){
// 容错处理
if(dst == NULL || src == NULL){
return NULL;
}
unsigned char *pdst = (unsigned char *)dst;
const unsigned char *psrc = (const unsigned char *)src;
//判断内存是否重叠
bool flag1 = (pdst >= psrc && pdst < psrc + count);
bool flag2 = (psrc >= pdst && psrc < pdst + count);
if(flag1 || flag2){
// 倒序拷贝
while(count){
*(pdst+count-1) = *(psrc+count-1);
count--;
}//while
}
else{
// 正序拷贝
while(count--){
*pdst = *psrc;
pdst++;
psrc++;
}//while
}
return dst;
}
int main(){
// 内存重叠
char c1[] = "hello world";
memmove(c1+3, c1, 8);
cout<<"memmove result:"<<c1<<endl;
// 内存不重叠
char c2[] = "hello world";
char c3[] = "love you";
memmove(c2,c3,8);
cout<<"memmove result:"<<c2<<endl;
}
区别
这两个函数都是将s2指向位置的n字节数据拷贝到s1指向的位置,区别就在于关键字restrict, memcpy假定两块内存区域没有数据重叠,而memmove没有这个前提条件。如果复制的两个区域存在重叠时使用memcpy,其结果是不可预知的,有可能成功也有可能失败的,所以如果使用了memcpy,程序员自身必须确保两块内存没有重叠部分。
示例
正常情况下,即使内容有重叠,src的内容也可以正确地被拷贝到了dest指向的空间。
这种情况下,src的地址小于dest的地址,拷贝前3个字节没问题,但是拷贝第4,5个字节时,原有的内容已经被src拷贝过来的字符覆盖了,所以已经丢失原来src的内容,这很明显就是问题所在。
那这个问题是怎么解决的呢?
memmove的实现方案
一种方法是把src
复制一块临时区域temp
,再拷贝进dest
。
#include <stddef.h> /* for size_t */
#include <stdlib.h> /* for memcpy */
void *memmove(void *dest, const void *src, size_t n)
{
unsigned char tmp[n];
memcpy(tmp,src,n);
memcpy(dest,tmp,n);
return dest;
}
上面这种方法显然效率比较低。其实我们可以从后面开始拷贝字符串。
也就是两个都分别前后移动count个单位,再向前走就可以了。
关键代码
void* memmove(void* dst,const void* src,size_t count)
{
assert(NULL !=src && NULL !=dst);
char* tmpdst = (char*)dst;
char* tmpsrc = (char*)src;
if (tmpdst <= tmpsrc || tmpdst >= tmpsrc + count)
{
while(count--)
{
*tmpdst++ = *tmpsrc++;
}
}
else
{
tmpdst = tmpdst + count - 1;
tmpsrc = tmpsrc + count - 1;
while(count--)
{
*tmpdst-- = *tmpsrc--;
}
}
return dst;
}
参考资料
C 运算符,位运算,逻辑运算
参考——仰视源码,实现memmove
转自——memcpy与memmove的区别
memcpy说明
memmove说明
代码在线运行网站
memcpy与memmove的区别