c语言memcpy与memmove区别

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,程序员自身必须确保两块内存没有重叠部分。
示例
图片1
正常情况下,即使内容有重叠,src的内容也可以正确地被拷贝到了dest指向的空间。
图片2
这种情况下,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的区别

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值