探究一下:使用memcpy函数能不能自己拷贝自己

简介: 探究一下:使用memcpy函数能不能自己拷贝自己

上篇博客,笔者详细的介绍了memcpy内存拷贝函数的使用及其模拟实现,到目前为止,引起了众多关注!


但是,仍有不少的读者对于笔者在上篇文章的最后所留的问题保持疑惑!!所以,笔者在此打算写一篇文章,来进行简单的解析,说明一下原因:


0a2653c851af460fa595bd959398a8f1.png


上面的问题,主要还是指:思考一下:对于数组:int  arr[]={1,2,3,4,5,6,7,8,9,10};能不能将1,2,3,4,5拷贝到3,4,5,6,7的位置??从而结果为:1,2,1,2,3,4,5,8,9,10呢???  这个答案是不能,但是为什么不可以呢??下面将是笔者的具体解析内容,请各位老铁仔细思考哟!!!


由于进行描写不是很方便,所以下面是笔者在画图板上面的内容!


2d65d23f6d4748949b924e4057485923.png


请大家仔细思考一下!!


由于在memcpy函数拷贝的时候,是按照字节来进行拷贝的!!所以,会进行一个一个int类型的拷贝,拷贝的数字将会覆盖原来的数字!所以,才会导致上面的结果!!请大家分析一下!就能得出上述结论!!


对于上述代码的实现过程为:


#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memcpy(void* dest, void* src, size_t num)
{
  void* ret = dest;
  assert(dest);
  assert(src);
  while (num--)
  {
  *(char*)dest = *(char*)src;
  dest = (char*)dest + 1;
  src = (char*)src + 1;
  }
  return ret;
}
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  my_memcpy(arr1 + 2, arr1, 20);
  int i = 0;
  for (i = 0; i < 10; i++)
  {
  printf("%d ", arr1[i]);
  }
  return 0;
}

当然,这个代码,还是借用了memcpy内存拷贝的模拟实现!!所以还是希望各位老铁能够欣赏一下笔者的上篇博客!!


在上述代码里面:my_memcpy(arr1 + 2, arr1, 20);  arr1是指数组的首元素!!arr1+2是:指向数组的第三个元素!!


6de278e6d6694ce5bb08e7e842b7e74b.png


经过上面的画图分析过程,想必代码的运行结果也是显而易见的!!


12c3b7f3f8814309a195c64f051d4445.png


总结:当源头与目的地的空间有重合的部分,会被覆盖掉!!所以显得上面的代码,不是很合理!!


下面笔者来带领大家具体情况具体分析一下!!请仔细思考!


对于数组arr1[]={1,2,3,4,5,6,7,8,9,10}我们可以:从一下方面进行考虑:


1.将1,2,3,4,5放到3,4,5,6,7的位置 ,我们可以用从后往前放的方法来实现目地!!


第一步:将5放在7的位置,第二步:将4放在6的位置,第三步:将3放在5的位置……这样依次按照顺序来实现!!


34e8d716411043c08c7ffba9fbba23de.png


2.但是:对于所有的部分都进行这种从后往前放的顺序,又不一定都对! 就比如:将4,5,6,7,8放在1,2,3,4,5的位置处!


按照从后往前放的方式来进行思考的话:那么:第一步:将8放在5的位置,第二步:将7放在4的位置,第三步:将6放在3的位置,第四步:将5放在……(注意此时5的位置处,放置的不再是5了,已经被前面的8替代!!所以显得非常不合理!!)


92ba0822ed0b46e1ae72df8a17d3a45b.png


所以对于这种的拷贝情况,我们应该这样思考:从前往后放!!


d79b274929334152a6d38be91e2d1be3.png


经过上面的两个案列,想必各位老铁也能看出来:复杂!!


总结:有时候需要从前往后拷贝!!


           有时候需要从后往前拷贝!!


因此,笔者进行粗略的总结一下 :


dfc80ca9d8004e6c9ddc00e8448ffc6a.png


因此,对于memcpy函数的模拟实现的更加完整的代码为:


#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memcpy(void* dest, void* src, size_t num)
{
  void* ret = dest;
  assert(dest);
  assert(src);
  if (dest < src)
  {  //在这里:比较的是地址!!
  while (num--)
  {
    //从前往后
    *(char*)dest = *(char*)src;
    dest = (char*)dest + 1;
    src = (char*)src + 1;
  }
  }
  else
  {
  while (num--)
  {
    *((char*)dest + num) = *((char*)src + num);
    //从后往前
  }
  }
  return ret;
}
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  my_memcpy(arr1 + 2, arr1, 20);
  int i = 0;
  for (i = 0; i < 10; i++)
  {
  printf("%d ", arr1[i]);
  }
  return 0;
}

上述代码的运行结果为:


0a2653c851af460fa595bd959398a8f1.png


其实,在C语言中,重叠部分的内存拷贝是交给:memmove函数实现的!!


因此对于memmove函数的模拟实现,便是将上面的代码,改改名字罢了!!在此,笔者就不再更改了!!


因此: memcpy只需要实现不重叠部分内存拷贝就可以了!


           memmove是需要实现重叠部分内存拷贝的!!


相关文章
|
3月前
|
编译器
拷贝对象时编译器的一些优化
拷贝对象时编译器的一些优化
|
2月前
|
机器学习/深度学习 算法 搜索推荐
|
4月前
|
存储 安全 C语言
C语言中的模拟按引用调用技术
C语言中的模拟按引用调用技术
26 0
|
4月前
|
存储 前端开发 编译器
【C语言】memmove()函数(拷贝重叠内存块函数详解)
【C语言】memmove()函数(拷贝重叠内存块函数详解)
82 1
|
编译器 C++ 容器
C++移动语义及拷贝优化
C++移动语义即提出了一个右值引用,使用`std::move`可以强制将左值引用转为右值引用。而对于右值引用,程序可以调用移动构造函数进行对象的构造,减少了原来调用拷贝构造函数的时候很大的开销。移动构造函数和移动赋值运算符的实现即是对象所有权的转让,让那些左值对象(临时对象)变成右值对象的过程。 编译器的拷贝优化确实效率很高,但是不能保证总是成功实施的。所以,好的编程习惯应该是对于自定义的类最好添加移动构造函数,重载移动赋值运算符。这样编译器的拷贝优化不成功的时候,可以调用移动构造减轻复制的开销,提高程序运行的效率。
79 0
普通函数中的this指向问题解决方案_this
普通函数中的this指向问题解决方案_this
35 0
模拟实现库函数strcpy,对strcpy的进一步理解(深刻理解重叠问题,防止内存与源重叠)
模拟实现库函数strcpy,对strcpy的进一步理解(深刻理解重叠问题,防止内存与源重叠)
|
程序员
拷贝函数的优化
拷贝函数的优化
55 0
|
C语言
C语言中基础(有关数据拷贝的函数,例:strcpy,strncpy,memcpy,memove库函数的实现和应该注意的小细节)
C语言中基础(有关数据拷贝的函数,例:strcpy,strncpy,memcpy,memove库函数的实现和应该注意的小细节)
|
C语言
由浅入深C系列四:memset/memcpy源码分析,为什么这二个函数的目的字符串在前面,源字符串在后面?
memset/memcpy源码分析,为什么这二个函数的目的字符串在前面,源字符串在后面?