C语言|C语言中内存函数的使用及其模拟实现(memcpy memmove memset)

mencpy的使用 所有的内存函数使用都要包含头文件string
C语言|C语言中内存函数的使用及其模拟实现(memcpy memmove memset)
文章图片

memcpy函数的功能:就是将 num 个字节数的源空间 source 的数据拷贝到目标空间destination 上去,返回值拷贝之后的数据。

  1. 这个函数很特别,可以拷贝任意的数据类型,不像我们之前了解的strcpy函数,只能拷贝字符串;
  2. 所以当我们向拷贝任意类型的数据时候可以使用这个函数,注意要确保拷贝过去字节的个数是要匹配上的;
  3. 这个函数原则上是拷贝不重叠的数据的,但是有一些编译器上实现memcpy是支持拷贝重叠数据的。为了保证通用性,我们使用该函数拷贝数据时候,默认前提是拷贝不重叠的数据。
memcpy的使用案例:
int arr1[]={ 1,2,3,4,5,6,7,8,9,10}; //初始化1-10的整形数 int arr2[20] = { 0}; //初始化20个零的数据//拷贝arr1空间的40个字节给arr2 memcpy(arr2,arr1,sizeof(int)*10); //拷贝之后的结果:arr2 = {1,2,3,4,5,6,7,8,9,10,0,0,0,0,0,0,0,0,0,0};

memcpy模拟实现 前提我们必须直到 menmcpy函数完成的功能是:不重叠的数据拷贝。
C语言|C语言中内存函数的使用及其模拟实现(memcpy memmove memset)
文章图片

设计思路:
由于是任意类型的拷贝,所以我们拷贝数据类型的字节数是不知道的,所以当我们进行对内存操作时候,只能将void* 转换为 char* 操作,因为 char* 能够操作一个字节,能够满足传进来的 nums个字节做 减 1的操作;
所以我们只要保持一个一个字节的拷贝过去即可。
/** * dest:目标空间 * sour:愿空间 * nums:要拷贝sour空间nums个字节数到dest空间 */ void* my_memcpy(void* dest,const void* sour,size_t nums) { void* temp = dest; while(nums--) {//将sour空间内容一个字节一个字节的拷贝过去dest空间 *(char*)dest = *(char*)sour; //完成依次拷贝后,dest和sour指针往前移动; dest = (char*)dest +1; sour = (char*)sour +1; } //退出循环后,表示nums个字节全部拷贝完毕 return temp; }

memmove的使用 memmove和memcpy实现的功能是一模一样的,但是有一个区别就是,memmove是支持拷贝重叠的数据的。
所以说memcpy能干的事 memmove都能干,至少在使用内存拷贝的时候,我们更加倾向使用memmove;
【C语言|C语言中内存函数的使用及其模拟实现(memcpy memmove memset)】memmove的可以拷贝重叠数据的使用,和memcpy不能拷贝重叠数据使用的对比
int arr1[]={ 1,2,3,4,5,6,7,8,9,10}; //初始化1-10的整形数//尝试使用memcpy拷贝重叠的数据 //尝试将arr1中的1,2,3,4,拷贝到arr1 的 arr[2] 和arr[3] ,arr[4],arr[5]的位置 //预期的结果是:1,2,1,2,3,4,7,8,9,10 //而实际的结果却是:1,2,1,2,1,2,7,8,9,10 memcpy(arr1+2,arr1,sizeof(int)*4); *********************************************************** *********************************************************** *********************************************************** int arr1[]={ 1,2,3,4,5,6,7,8,9,10}; //初始化1-10的整形数//尝试将arr1中的1,2,3,4,拷贝到arr1 的 arr[2] 和arr[3] ,arr[4],arr[5]的位置 //预期的结果是:1,2,1,2,3,4,7,8,9,10 //实际的结果也是: 1,2,1,2,1,2,7,8,9,10 memmove(arr1+2,arr1,sizeof(int)*4);

**话说不同编译器的对memcpy的实现可能不一样,即对于memcpy来说,C语言就规定它要做的就是完成不重叠的拷贝,但是有些编译器可能会实现memcpy完成了重叠的拷贝。**所以要注意这个点:
C语言|C语言中内存函数的使用及其模拟实现(memcpy memmove memset)
文章图片

C语言规定已经表明了态度,要干完成重叠拷贝的函数就要使用memmove.这更加安全。
memmove模拟实现 对于memmove由于要拷贝的数据是有重叠问题的,所以要思考各种情况:
情况一:
C语言|C语言中内存函数的使用及其模拟实现(memcpy memmove memset)
文章图片

很明显对于这种情况:我们应该从源数据空间的前面开始到一个数据一个数据的往目标空间里面拷贝,这样就可以完美的把sour数据拷贝到dest空间去。
简而言之:从 sour的前面往后面拷贝过去。
情况二:
C语言|C语言中内存函数的使用及其模拟实现(memcpy memmove memset)
文章图片

很明显:我们将源空间的数据从最后面的数据,一个字节一个字节的往dest后面数据拷贝过去,这样就可以完美拷贝过去了
简而言之就是:从源数据的后面往前面拷贝。
情况三:
C语言|C语言中内存函数的使用及其模拟实现(memcpy memmove memset)
文章图片

很明显,对于不重叠的数据来说:无论是将源数据从前往后拷贝,还是从后往前拷贝都可以拷贝成功。
对于上面三种情况,我们如何转换为代码的形式呢?
我们观察
  1. 情况一:当源数据指针 sour 落在了目标空间dest的右侧时候:我们就将源数据从前往后拷贝,对应代码表示 落在右侧的意思:sour > dest;
  2. 情况二:当源数据指针sour落在了目标空间dest的左侧侧时候:我们就将源数据从后往前拷贝,对应代码表示 落在左侧的意思:sour < dest;
  3. 情况三:无论是源数据 sour 落在了目标空间dest的左侧还是右侧,只要不重叠,源数据可以从前往后拷贝,也可以从后往前拷贝;
模拟实现memove
/** * dest:目标空间 * sour:愿空间 * nums:要拷贝sour空间nums个字节数到dest空间 */ void memmove(void* dest,void* sour,size_t nums) { void* temp = dest; //用来返回的地址变量 if(sour > dest) //表明源空间落在了目标空间的右侧 {//在if条件成立的前提下:将源数据从前往后拷贝 while(nums--) //nums先使用,后减减,所以说进入循环的muns是减减后的值 {*(char*)dest = *(char*)sour; dest = (char*)dest + 1; dest = (char*)sour + 1; } } else //来到这里是 sour< dest或者是 sour在 dest的左侧右侧不重叠任意位置 {//sour从后往前拷贝 while(count--) {//下面的代码意思是,在dest空间中,最后一个字节的位置, //开始向前一个字节一个字节的拷贝; //最后一个字节的位置,可以用指针加整数的方式找到; *((char*)dest + count) = *((char*)sour + count); } } return temp; }

上面就是模拟实现啦!
测试数据:结果自己在编译器跑一跑,看看监视的结果,是否符合预期,就可以验证答案啦。
int arr1[]={ 1,2,3,4,5,6,7,8,9,10}; //初始化1-10的整形数my_memmove(arr1+2,arr1,16); //在重叠的前提下:源空间数据落在了目标空间数据的左边情况my_memmove(arr1,arr1+2,16); //在重叠的前提下:源空间数据落在了目标空间数据的右边情况my_memmove(arr1,arr1+4,16); //不重叠的前提下:源空间无所谓落在目标空间左右my_memmove(arr1+4,arr1,16); //不重叠的前提下:源空间无所谓落在目标空间左右

memset的使用 memset是内存设置函数,操作的是内存的每一个字节。
函数功能:给ptr空间设置 num个字节的value。其实就是修改ptr空间里面的内容为value,操作的内存是num个字节大小。
C语言|C语言中内存函数的使用及其模拟实现(memcpy memmove memset)
文章图片

使用案例

/* memset example */ #include #include int main () {char str[] = "almost every programmer should know memset!"; memset (str,'-',6); //设置了str的前六个字节为- puts (str); return 0; }

结果:
------ every programmer should know memset!

就不模拟实现memset啦,会使用就可以。

    推荐阅读