模拟实现库函数memcpy--复制内存块。详细理解内存重叠及精准复制问题

简介: 模拟实现库函数memcpy--复制内存块。详细理解内存重叠及精准复制问题

一.对库函数memcpy的了解



通过在MSDN或者cplusplus网站上检索memcpy

aef899bd9c784d0ead7b058d500df120.png


通过对memcpy的检索,可以初步了解到memcpy以下信息:


1.有三个参数,目标指针,源头指针均为泛型指针 void* () (具有通用性,且不能直接被解引用

2.该函数不检查源中是否有任何终止空字符 , 它始终精确地复制数字字节

3.为避免溢出,目标参数和源参数所指向的数组的大小应至少为 num 个字节

4.目标数据和源数据不能重叠


二.模拟实现库函数memcpy



思路分析:


1.通过对库函数memcpy的了解,它的前面两个参数都是void类型的泛型指针,无法直接解引用,并且用户使用时候可能传入的是int 、char、float等类型中的一种,这对传入后复制多少个字节的内容带来了一定难度,因此这个void的参数应该转换为什么类型是我们应该首要考虑的


假设如下图,将参数强转为int类型进行操作,那么arr+1将跳过四个字节,如果传入的size_t num=6,那么将对第二个字节的内容读取前面两个字节的内容,进行解引用以后会产生影响,因此,在这应当选择操作力度最小的char类型进行操作,一次跳过一个字节,就能很好的规避上面的问题

580f0e9951214bc9be21175d3512a3fb.png


2.循环num次,将源头数据的内容覆盖到目标数组中


代码如下:

#include <stdio.h>
#include <assert.h>
void* my_memcpy(void* str1, const void* str2, size_t num)
{
  assert(str1 && str2);
  void* p = str1;//返回目标地址,记录目标地址起始位置
  while (num--)
  {
    *(char*)str1 = *(char*)str2;
    str1 =(char*)str1+1;
    str2 =(char*)str2+1;
  }
  return p;
}
int main()
{
  char arr1[] = "abcdef";
  char arr2[20] = "hh";
  my_memcpy(arr2, arr1, 3);
  printf("%s\n", arr2);
  return 0;
}


运行结果如下:

04998ff41820424ea531acd2f2f334a8.png


三.对库函数memcpy的具体说明



1.如果需要复制int类型的数据该如何


若为其他类型,在传参时,传入的字节数尤为重要,只需要将你想要的复制的元素个数乘上类型的大小即可

#include <stdio.h>
#include <assert.h>
void* my_memcpy(void* str1, const void* str2, size_t num)
{
  assert(str1 && str2);
  void* p = str1;//返回目标地址,记录目标地址起始位置
  while (num--)
  {
    *(char*)str1 = *(char*)str2;
    str1 =(char*)str1+1;
    str2 =(char*)str2+1;
  }
  return p;
}
int main()
{
  int arr1[] = {1,2,3,4};
  int arr2[20] = {0};
  int sz=sizeof(arr1)/sizeof(arr1[0]);
  my_memcpy(arr2, arr1, 3*sizeof(int));
  for(i=0;i<sz;i++)
  {
     printf("%d ",arr2[i]);
  }
  return 0;
}


运行结果如下:

4ef1f1350d7743ab8ff9bd09c7ad774d.png


2.它始终精确地复制数字字节


对于开头提到的该函数不检查源中是否有任何终止空字符 ,它始终精确地复制数字字节。这句话表明无论源头数据是否具有\0,它会连同\0一同复制到目标数据中。


代码如下:

int main()
{
  char arr1[] = "a\0bcdef";
  char arr2[20] = "hh";
  my_memcpy(arr2, arr1, 3);
  printf("%s\n", arr2);
  return 0;
}


运行结果如下:

0a95ed3637ed4d38aa61e9731223ee2d.png


可以看到,原本应当打印a\0b,由于打印的时字符串,因此只打印了a,通过观察内存可以验证该结果


调试观察内存可以看到:

8b265dbeab6648829ce9d408e3758a39.png


因此,库函数memcpy会将num个字节的内容复制到目标空间中,终止符也可能会被复制。


3.覆盖目标空间的内容

通过(2)上面代码的运行结果可以看到,arr2中原本有“hh”这个字符串,但是最后结果内存中却只有a\0b无论arr2这个源头空间中原本是否有数据,memcpy都会将其覆盖。


4.目标数据和源数据不能重叠(重点理解)


当出现下面这种情况时,数据会出现丢失

例如:

int main()
{
  char arr1[] = "abcdef";
  char arr2[20] = "hh";
  my_memcpy(arr1+2, arr1, 4);
  //         str1    str2
  printf("%s\n", arr1);
  return 0;
}

52d3e75fcff64b96a852ac7d1ab54c5c.png



当a覆盖到c的位置时,c已经被覆盖成了a,st2指向原本c的位置时,拷贝给st1的内容就是a了,不是原本的c造成了数据的丢失:

image.png


运行结果如下:


image.png


因此,目标数据与源头数据的内存不能重叠,否则就会发生数据拷贝内容被覆盖,数据出现偏差。


5.防止越界


由于库函数memcpy时将源头的num个字节的内容覆盖到目标内存中,因此,目标内存有可能出现越界情况。

当出现下面情况时,会发生越界崩溃

32e07ad5d2b341bba26351079636ca89.png


int main()
{
  char arr1[] = "abcdef";
  char arr2[20] = "hh";
  my_memcpy(arr1+4, arr1, 4);
  //         str1    str2
  printf("%s\n", arr1);
  return 0;
}


st1向后访问num个字节以后比sizeof(arr1)大的情况下,会发生越界,应当使用时防止该情况发生。

103558414d8042c1bbe47efd232c4a0c.png



运行结果如下:

c7d3febfe0a24ef0876e4c76f1ef9170.png


相关文章
|
2月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
36 3
|
2天前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
24 6
|
2月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
2月前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
2月前
|
存储 程序员 编译器
C语言——动态内存管理与内存操作函数
C语言——动态内存管理与内存操作函数
|
2月前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
27 0
|
2月前
|
C语言 C++
c语言回顾-内存操作函数
c语言回顾-内存操作函数
41 0
|
2月前
|
存储 C语言 C++
来不及哀悼了,接下来上场的是C语言内存函数memcpy,memmove,memset,memcmp
本文详细介绍了C语言中的四个内存操作函数:memcpy用于无重叠复制,memmove处理重叠内存,memset用于填充特定值,memcmp用于内存区域比较。通过实例展示了它们的用法和注意事项。
71 0
|
2月前
一刻也没有为它哀悼~接下来登场的是动态内存分配的malloc与realloc以及free函数
一刻也没有为它哀悼~接下来登场的是动态内存分配的malloc与realloc以及free函数
77 0
|
4月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
394 0