• 赶紧进来看看---带来三种内存操作函数以及每种函数的模拟实现练习


    本文详细介绍了三种内存操作函数–memcpy(内存拷贝函数).memmove(内存移动函数).memset(内存设置函数).对内存空间里的指定个数的内存单元进行操作,学会这三个函数对内存和指针更进一步了解

    一.内存函数介绍

    memcpy—内存拷贝函数
    memmove—内存移动函数
    memset—内存设置函数
    这三种库函数可以直接访问内存,使用时应包含头文件#include

    1.memcpy内存拷贝函数

    memcpy内存拷贝函数是封装的一个库函数,主要实现将一段内存空间的数据拷贝到另一段内存空间里

    ①memcpy库函数的介绍

    右边是官网对memcpy的详细介绍->memcpy字符拷贝函数
    在这里插入图片描述
    该函数形参列表有三个形参.
    第一个参数类型是:void * 空类型指针 即对应的是要被修改的空间的起始地址,因为要拷贝的空间大小是未知的,即其地址类型也是不确定的,因此使用void *空类型指针变量接受其地址
    第二个参数类型是:void * 空类型指针 即对应要拷贝过去的内存空间的起始地址,因为其空间大小也是不确定的,用void *接受
    第三个参数类型是size_t 无符号整形类型 对应的是要拷贝的字节个数,即一个字节对应一个内存单元里的值,精确到每个内存单元的拷贝
    返回类型是:void ,表明调用该函数直接内存拷贝 ,不会返回值

    ②memcpy库函数的使用

    内存拷贝函数最神奇的地方是可以控制拷贝的字节个数,当我们拷贝字符串时,只想拷贝前字符串中前多个字符不想全部拷贝时,而使用strcpy字符串拷贝函数会全部拷贝,满足不了需求,而内存拷贝函数可以实现多字节拷贝,实现将多个字符拷贝的功能↓

    在这里插入图片描述

    上面代码实现了通过memcpy内存拷贝函数将字符串前三个字符拷贝到另一个字符数组内…

    但是:当我们实现同一数组里不同位置内存空间的拷贝,即两个内存空间出现重叠的情况下,memcpy函数可能会出现两种不同情况…

    在这里插入图片描述

    上面代码是将数组第一个元素到第三个元素这三个字符拷贝到第二个元素到第四个元素内,出现了重叠的情况,会出现下面两种情况

    在这里插入图片描述

    为什么会有上面两种情况呢,因为拷贝内存可以看作是一个个内存单元进行拷贝,先将a的内存拷贝的第二个元素处,将第二个元素拷贝到第三个元素时会发现第二个元素已经变为了a,此时第三个也会被拷贝为a,第四个也会为a,最后拷贝了三个字节后发现空间变成了”aaaae“
    字符串

    这是memcpy设计时出现重叠部分可能会出现的重叠拷贝的情况,
    而第一种情况是满足我们需求的将abc放到第二到第四元素位置为字符串”aabce“,memcpy库函数实现的要求重叠区域的拷贝可能会发生第一种情况,但是不同编译器对库函数要求不同,比如vs会将memcpy进行优化,使其能够拷贝重叠区域不会重叠数据,我们不能确保每个编译器都会对这个函数进行优化…
    但是能够实现重叠区域数据拷贝的函数也有,那就memmove内存移动函数

    ③memcpy库函数的使用注意事项

    1.函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
    2.这个函数在遇到 ‘\0’ 的时候并不会停下来。
    3.如果source和destination有任何的重叠,复制的结果都是未定义的。

    2.memmove内存移动函数

    memmove库函数,主要是弥补memcpy内存拷贝函数中内存重叠情况的问题,主要运用于内存重叠情况下的内存拷贝…

    ①memmove库函数介绍

    右边是官网对其的介绍->memmove内存移动函数

    在这里插入图片描述

    该函数的形参类型 和返回类型和memcpy内存拷贝函数一致
    但是在实现上增加了功能:主要是根据两个内存起始地址的高低采取不同的拷贝方式来避免内存拷贝重叠的情况…

    当源头指针大于目的地指针时,会采取从前往后一个字节一个字节的对应拷贝,此时不会发生重叠
    当源头指针小于目的地指针时,会采取从后往前一个字节一个字节的对应拷贝,此时不会发生重叠

    ②.memmove库函数的使用

    在这里插入图片描述

    用这个库函数实现上面重叠区域情况下的内存拷贝不会出现"aaaae"数据重叠这种情况,会正常的将abc放到第二到四元素位置

    ③.memmove的使用注意事项

    1.和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
    2.如果源空间和目标空间出现重叠,为了避免发生错误,就得使用memmove函数处理。

    3.memset内存设置函数

    memset库函数主要实现将指定的内存空间里在一个多个内存单元内设置自己想要的数据
    可以对复杂的内存空间进行初始化

    ①.memset库函数的介绍

    右边是官网对memset的介绍->memset内存设置函数
    在这里插入图片描述

    memset库函数形参列表有三个参数
    第一个参数类型:void * 空类型指针,因为不确定需要设置的内存空间的字节即类型大小,用空类型指针变量接收其起始地址
    第二个参数类型:int 表示要设置的数据大小(一般要在-128到127之间)因为一个字节的内存空间大小就8位二进制数,超过了会发生数据截断
    第三个参数类型:size_t无符号整形类型,表示要设置的字节大小即多少个内存单元
    返回类型为空,表示调用该函数不会返回值

    ②.memset库函数的使用

    在这里插入图片描述

    上面函数实现了从数组首元素起始地址开始将后面3个内存单元的值设置为了字符’6’即ASCLL码值54,即字符数组里第一到三的元素设置为了字符6,通过打印输出了"666##"

    ③memset库函数的使用注意事项

    1.内存设置函数会根据自己给的字节大小往后设置字节大小个数的内存单元个数
    2.使用该函数注意设置的字节个数即内存单元的个数,不要超过了要设置的内存最大大小导致越界访问…

    二.内存函数的模拟实现

    根据这三种内存函数的特性,功能能实现.进行模拟相似的函数

    1.memcpy库函数的模拟实现

    #include
    #include
    #include
    void my_memcpy(void* arr, const void* str, size_t num) //用空指针类型接受类型不确定的指针
    {
    	assert(arr != NULL && str != NULL);  //断言函数避免arr和str是空指针
    	int i = 0;
    	for (i = 0; i < num; i++)
    	{
    		*((char*)arr + i) = *((char*)str + i); //  将arr 和str 指针强转为char*类型此时指针解引用访问其地址上的一个内存单元 +i
    	}
    }
    int main()
    {
    	char arr[] = "abcde";
    	char str[] = "ABCDE";
    	my_memcpy(arr, str, 3);
    	printf("%s", arr);
    	return 0;
    }
    

    在这里插入图片描述

    将地址强制类型转换为char* 字符指针类型,通过一个循环从起始地址往后直到第num个元素地址,解引用对应的每一个内存单元进行赋值操作,最后实现的指定内存空间num个内存单元的拷贝…

    但是这样设计也就出现了memcpy的弊端,当第一个实参为arr+1,第二个实参为arr时,会发现存在内存重叠情况,目的地指针比源头指针大,此时从前拷贝会发生第一个数据拷贝到第二个内存单元里,而第二个内存单元数据拷贝到第三个内存单元时,第二个内存单元的数据变为了第一个内存单元.就出现了数据重叠的情况…

    2.memmove库函数的模拟实现

    memmove主要考虑了两个内存空间重叠的情况下数据的拷贝

    #include
    #include
    #include
    void my_memmove(void* arr,  void* str ,size_t num)
    {
    	assert(arr != NULL && str != NULL);
    
    	if (str < arr)    //判断当str源头指针 比目的地指针小时从后往前拷贝,不会造成数据重叠
    	{
    		while (num--)   // 当num为真时 进入循环 即从要拷贝的最后一个字节数开始 每拷贝一次 往前移
    		{
    			*((char*)arr + num) = *((char*)str + num);   //从要拷贝的空间最后一个内存单元往前拷贝开始拷贝,直到拷贝完第一个字节内存单元后结束
    		}
    	}
    	else  //而当源头指针大于等于目的地指针时,从前往后拷贝 不会造成数据重叠
    	{
    		int i = 0;
    		for (i = 0; i < num; i++)
    		{
    			*((char*)arr + i) = *((char*)str + i);
    		}
    	}
    
    }
    int main()
    {
    	char arr[] = "abcde";
    	my_memmove(arr + 1, arr, 3);
    	printf("%s", arr);
    	return 0;
    }
    

    在这里插入图片描述

    memmove模拟实现 比memcpy多一种拷贝数据的方法,即从后往前拷贝,当出现重叠情况,后面一块空间数据拷贝到前面一块空间时采取从前往后一个字节一个字节拷贝,当前面一块空间数据拷贝到后面一块空间时 ,采取从后往前拷贝.
    根据这个设计可以实现两个重叠的内存区域拷贝数据不会造成数据反复覆盖重叠的情况
    但是比memcpy多了判断,当我们需要拷贝不重叠的内存空间数据时优先选择memcpy
    当我们需要拷贝重叠内存空间的数据时选择memmove函数…

    3.memset库函数的模拟实现

    模拟实现memset函数 对内存空间里指定字节个数的内存单元设置数据

    #include
    #include
    #include
    void my_memset(void* arr, int value, size_t num)
    {
    	assert(arr != NULL);
    	int i = 0;
    	for (i = 0; i < num; i++)  //i从0开始到num结束
    	{
    		*((char*)arr + i) = value;  //将arr指针强转为char * 解引用访问的是一个内存单元 依次从第一个到第num个内存单元的数据设置为value
    	}
    }
    int main()
    {
    	char arr[] = "#####";
    	my_memset(arr, '6', 3);
    	printf("%s", arr);
    	return 0;
    }
    

    在这里插入图片描述

    通过一个循环,将传过来的内存起始地址从第一个内存单元开始往后依次赋值上要设置的数据,完成内存设置功能

    三.总结

    本文详细介绍了三种内存函数(memcpy,memmove,memset)以及其对应的模拟实现,这三种函数可以精细到对指定个数内存单元进行拷贝或者设置等操作,相比字符串拷贝函数对内存操作权限更高… 学会使用内存函数,对内存的掌握就更进一步…

    在这里插入图片描述

    写文不易,给个一键三连支持下叭~

  • 相关阅读:
    Java深拷贝与浅拷贝
    zookeeper + kafka集群搭建详解
    JavaWeb 学习笔记 1:MyBatis
    计算机网络
    C语言只推荐这1本宝藏书,你读过吗?
    Gitlab仓库部署
    前端实现chatGpt流式输出 - SSE
    王道数据结构5(树与二叉树)
    Solr安装使用教程
    计算机网络
  • 原文地址:https://blog.csdn.net/lch1552493370/article/details/127033907