• 【内存操作函数内功修炼】memcpy + memmove + memcmp + memset(四)


    在这里插入图片描述

    在这里插入图片描述

    1. memcpy - 内存拷贝

    前面我们学习了 strcpystrncpy,用于拷贝字符串的库函数,但是,假设我们需要拷贝其他类型的数据呢?比如 浮点型整型结构体

    这个时候就引出了 memcpy,它不关系是什么类型的数据,只要是存放在内存中的,都可以进行拷贝。

    🍑 函数介绍

    void* memcpy(void* dest, const void* src, size_t num);
    
    • 1

    memcpy 函数是一个用于拷贝两个不相关的内存块的函数。

    memcpy 函数会从 src 的位置开始向后复制 num 个字节的数据到 dest 的内存位置,并返回 dest 的首地址。

    注意:

    1、memcpy 函数在遇到 '\0' 的时候并不会停下来。

    2、如果 srcdest 有任何的重叠,复制的结果都是未定义的。

    3、memcpy 函数在拷贝的时候,不知道会被用来拷贝什么样的数据类型,所以参数的类型为 void*(可接收任意类型指针)。

    📝 代码实现

    假设要把 str1 数组中的前 5 个元素拷贝到 str2

    #include 
    #include 
    
    int main()
    {
    	int str1[] = { 1,2,3,4,5,6,7,8 };
    	int str2[5] = { 0 };
    
    	memcpy(str2, str1, 5 * sizeof(str1[0]));
    
    	for (int i = 0; i < 5; i++) {
    		printf("%d ", str2[i]);
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    🌟 运行结果
    在这里插入图片描述

    🍑 模拟实现

    假设要把 str1 数组中的后 5 个元素拷贝到 str2 中👇
    在这里插入图片描述
    进入 my_memcpy 函数体,首先保存 dest 的起始位置,便于之后返回。
     
    然后循环 num 次,每次将 src 中的一个字节的内存数据拷贝到 dest 中的对应位置;
     
    然后 destsrc 指针后移继续拷贝,拷贝结束后返回 dest 原来的首地址即可。
     
    在对 destsrc 指针进行操作时,要先将它们强制类型转换为 char* 类型的指针。
     
    (char* 类型的指针可以向后访问一个字节的内容)

    📝 代码实现

    #include 
    #include 
    
    void* my_memcpy(void* dest, const void* src, size_t num) {
    	assert(dest && src);
    	void* start = dest;
    	while (num--) {
    		*(char*)dest = *(char*)src;
    		dest = (char*)dest + 1;
    		src = (char*)src + 1;
    	}
    	return start;
    }
    
    int main()
    {
    	int str1[] = { 1,2,3,4,5,6,7,8,9,10 };
    	int str2[5] = { 0 };
    
    	my_memcpy(str2, str1+5, 5 * sizeof(str1[0]));
    
    	for (int i = 0; i < 5; i++) {
    		printf("%d ", str2[i]);
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    🌟 运行结果
    在这里插入图片描述

    2. memmove - 内存拷贝

    memmovememcpy 的作用是一模一样的,但是唯一的差别就是 memmove 函数处理的源内存块和目标内存块是可以重叠的。

    memcpy 函数的源内存块和目标内存块是不可以重叠的。

    🍑 函数介绍

    void* memmove(void* dest, const void* src, size_t num);
    
    • 1

    注意:

    1、memmove 函数处理的源内存块和目标内存块是可以重叠的。

    2、如果源空间和目标空间出现重叠,就得使用 memmove 函数处理。

    📝 代码实现

    假设我们要把 str1 数组中的 1、2、3、4、5,拷贝放到 3、4、5、6、7 的位置

    #include 
    #include 
    
    int main()
    {
    	int str1[] = { 1,2,3,4,5,6,7,8,9,10 };
    
    	memmove(str1 + 2, str1, 20);
    
    	for (int i = 0; i < 10; i++) {
    		printf("%d ", str1[i]);
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    🌟 运行结果
    在这里插入图片描述

    🍑 模拟实现

    假设我们要把 str1 数组中的 1、2、3、4、5,拷贝放到 3、4、5、6、7 的位置👇
    在这里插入图片描述
    那么这个怎么模拟实现呢?其实很简单,往下看👇
    在这里插入图片描述
    这种情况是 倒着拷,但是还有一种,假设我们要把 str1 数组中的 3、4、5、6、7 拷贝放到 1、2、3、4、5 的位置👇
    在这里插入图片描述
    那么这个怎么模拟实现呢?其实很简单,往下看👇
    在这里插入图片描述
    所以我们要分类讨论,通过画图可以得知,可以分为三类情况;
     
    第一类:当 dest 指针位于 src 指针内存块左边,采用 从前向后 拷贝。
    在这里插入图片描述
    第二类:当 dest 指针位于 src 指针内存块里面,采用 从后向前 拷贝。
    在这里插入图片描述
    第三类:当 dest 指针位于 src 指针内存块右边,采用 从前向后从后向前 都可以。
    在这里插入图片描述
    **注:**当 dest 指针与 src 指针位于同一位置时不用拷贝。

    📝 代码实现

    #include 
    #include 
    
    void* my_memmove(void* dest, const void* src, size_t num) {
    	assert(dest && src);
    	void* start = dest;
    	
    	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 start; //返回dest的起始地址
    }
    
    int main()
    {
    	int str1[] = { 1,2,3,4,5,6,7,8,9,10 };
    
    	my_memmove(str1 + 2, str1, 20);
    
    	for (int i = 0; i < 10; i++) {
    		printf("%d ", str1[i]);
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    🌟 运行结果
    在这里插入图片描述

    3. memcmp - 内存比较

    🍑 函数介绍

    int memcmp(const void* buf1, const void* buf2, size_t count);
    
    • 1

    memcmp 函数是一个用于比较两个内存块大小的函数。

    它会比较从 buf1buf2 指针开始的 count 个字节;

    buf1 大于 buf2 的时候返回一个 大于0 的数;

    buf1 等于 buf2 的时候返回 0

    buf1 小于 buf2 的时候返回一个 小于0 的数。
    在这里插入图片描述

    📝 代码实现

    #include
    #include
    
    int main()
    {
    	int str1[] = { 1,2,3,4 };
    	int str2[] = { 1,2,4,5 };
    
    	int ret1 = memcmp(str1, str2, 8);
    	int ret2 = memcmp(str1, str2, 9);
    
    	printf("%d\n", ret1);
    	printf("%d\n", ret2);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    🌟 运行结果
    在这里插入图片描述
    VS2019 编译器下,内存采用的是 小端字节序 存储方式,str1str2 在内存中的存储形式如图所示👇
    在这里插入图片描述
    所以,当比较字前 8 个字节数时,str1 等于 str2,所以返回值为 0

    当比较字前 9 个字节数时,前 8 个字节是相等的,第 9 个字节是 str1 小于 str2,所以返回值为 小于0

    4. memset - 内存设置

    🍑 函数介绍

    void *memset( void *dest, int c, size_t count );
    
    • 1

    memset 函数可以将内存块的某一部分设置为特定的字符。

    第一个参数 dest 是开始设置内存的起始位置;

    第二个参数 c 是要将内存设置成的字符;

    第三个参数 count 是从起始位置开始需要设置的内存的字节数。

    注意: memset 函数设置内存的时候是一个字节一个字节地设置的。

    📝 代码实现

    假设我们要把 arr 数组中的前 10 个字节数设置为 0

    #include
    #include
    
    int main()
    {
    	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    
    	memset(arr, 0, 10);
    
    	for (int i = 0; i < 10; ++i) {
    		printf("%d ", arr[i]);
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    🌟 运行结果
    在这里插入图片描述
    VS2019 编译器下,内存采用的是 小端字节序 存储方式,arr1 在内存中的存储形式如图所示👇
    在这里插入图片描述
    这个和 memcmp 是比较有点类似,就是要逐个字节进行比较!

    5. 总结

    以上就是我们 C 语言中常用的内存操作函数,其实理解起来不难,重要是多加使用练习😛

  • 相关阅读:
    【数据结构】二叉树—二叉树镜面反转
    def和class的区别
    【1429】招生管理管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目
    低代码热潮下的冷思考:为何我们不能盲目追随
    计算机语言
    运动爱好者的专属耳机,轻巧时尚又好用,哈氪无界上手
    浅谈计算机领域顶会与顶刊的那些事
    ssm基于微信小程序的学习资料销售平台+ssm+uinapp+Mysql+计算机毕业设计
    人工智能研究的未来:20 年机器学习和深度学习的论文创意!
    抖音 Android 性能优化系列:Java 锁优化
  • 原文地址:https://blog.csdn.net/m0_63325890/article/details/125627370