【C语言 模拟实现memcpy函数、memcpy函数】

C语言程序设计笔记---027

C语言之模拟实现memcpy函数、memcpy函数

前言:

通过C语言内存函数的知识,这篇将对memcpy函数、memcpy函数进行深入学习底层原理的知识,并模拟实现对应功能。

/知识点汇总/
内存相关的函数

1.memcpy --strcpy

2.memmove

3.memset

4.memcmp --- strcmp

1、介绍memcpy函数

函数原型 :void *memcpy( void dest, const void src, size_t count ); ---- void 说明,兼容任意类型
函数功能:将源头内存的数据,拷贝到目标内存中,返回值类型为void

头文件 :<string.h>
使用注意事项

(1)、与strncpy不同的是,参数num和count。一个是指需要拷贝的元素个数,一个是指需要拷贝的元素字节大小

(2)、相比strcpy函数仅仅是操作字符串进行拷贝,而memcpy内存中的数据,不仅仅是操作字符。
示例代码如下

c 复制代码
#include <stdio.h>
#include <string.h>
int main()
{
	int arr1[10] = { 0 };
	int arr2[] = { 1,2,3,4,5 };
	memcpy(arr1, arr2, 20);//20 == 4*5
	int sz = sizeof(arr1) / sizeof(arr1[0]);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

1.1、模拟实现memcpy函数

c 复制代码
#include <stdio.h>
#include <string.h>
#include <assert.h>
//注意:void* 不能进行运算操作
//另外,这里必须使用(char*)进行操作,因为如果是其它类型就会造成数据上的丢失等问题 --- 联想类似于qsort一个个字节进行操作
void* my_memcpy(void* dest, const void* src, size_t sz)//sz拷贝多少个字节
{
	assert(dest && src);
	while (sz--)//以一个字节一个字节的拷贝
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;//注意:强制类型转换具备临时属性,所以需要(char*)dest+1来赋值保存数据,不能用dest++
		src =  (char*)src + 1;
	}
}
int main()
{
	int arr1[10] = { 0 };
	int arr2[] = { 1,2,3,4,5 };
	my_memcpy(arr1, arr2, 20);//20 == 4*5
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

解释说明

1.assert是断言,参数为指针,防止传参过来是空指针避免野指针的问题

2.用一个指针变量始终保存目标字符串的起始地址,以免目标起始地址发生改变,导致函数的返回值错误

3.sz--因为memcpy是以字节为单位进行的拷贝,所以为了兼容多种类型,就需要强转为char*类型之后再操作。(可回顾前面篇章,模拟qsort函数的内容

扩展 :memcpy对同一内存空间的操作问题

以我们自己模拟的memcpy函数,导致问题的原因:

调试可知,它是以一个一个字节进行的拷贝,

所以比如:对0x11223344进行拷贝为01操作是0x01010101达不到我们的预期,

而我们预期应该是0x11223301,然后下一个元素比如0x11224401

但是,库函数的memcpy在当前的编译器条件下,弥补了这样的错误问题。说明库函数作者完善的很全面,同时更复杂。

2、介绍memmove函数

函数原型 :void *memmove( void dest, const void src, size_t count );
函数功能:将源头内存的数据。拷贝到目标内存中,且与memcpy相比可以拷贝同一空间的数据,返回值类型为void

头文件 :<string.h>
使用注意事项

(1)、memcpy只能对不同空间内存之间的数据进行操作

即:不重叠内存的拷贝,可以使用memcpy,而重叠的内存空间则需要用memmove函数
示例代码如下

c 复制代码
#include <stdio.h>
#include <string.h>
int main()
{
	int arr1[10] = { 0 };
	int arr2[] = { 1,2,3,4,5 };
	memmove(arr1, arr2, 20);//20 == 4*5
	int sz = sizeof(arr1) / sizeof(arr1[0]);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

2.1、模拟实现memmove函数

c 复制代码
#include <stdio.h>
#include <assert.h>
void* my_memmove(void* dest, const void* src, size_t sz)
{
	assert(dest && src);
	char* ret = (char*)dest;
	if (dest < src)//从前向后拷贝
	{
		while (sz--)
		{
			*((char*)dest) = *((char*)src);
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else//从后向前拷贝
	{
		while (sz--)//sz-- 先用再减,即先判断sz=20为真,用完后减1进入循环,此时sz=19
		{
			*((char*)dest + sz) = *((char*)src + sz);
		}
	}
	return ret;
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr + 2, arr, 20);//20 == 4*5
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

解释说明

1.assert是断言,参数为指针,防止传参过来是空指针避免野指针的问题

2.用一个指针变量始终保存目标字符串的起始地址,以免目标起始地址发生改变,导致函数的返回值错误

3.dest < src表示目标空间地址在源地址前面,适合从源地址的高位开始拷贝,即从前向后拷贝

值得注意的是,因为memmove是以字节为单位进行的拷贝,所以为了兼容多种类型,就需要强转为char*类型之后再操作。(可回顾前面篇章,模拟qsort函数的内容

4.相反否则就是,目标空间地址在源地址后面,适合从源地址的低位开始拷贝,即从后向前拷贝

值得注意的是,对于源地址的最后一个数据地址是指针+sz从后向前偏移。

小结

1.memcpy只能对不同空间内存之间的数据进行操作

即:不重叠内存的拷贝,可以使用memcpy,而重叠的内存空间则需要用memmove函数

2.库函数可以实现,标准规定:

以memcpy来实现不重叠的内存的拷贝,且要求能达到60即可

以memmove来实现重叠内存的拷贝

3.但是,发现当前的编译环境下,memcpy同样能实现重叠内存的拷贝,远远满足60分的要求,100分,所以于编译环境相关,建议相应的功能交给适合的函数实现即可

3、结语

学习函数的最实用的方式就是用自己的逻辑简单实现一些类似的功能
半亩方糖一鉴开,天光云影共徘徊。
问渠哪得清如许?为有源头活水来。--朱熹(观书有感)

相关推荐
浪子明X13 小时前
十六位账本怎样发现传输差错:Internet Checksum 生活类比
开发语言
老当益壮梁奶奶14 小时前
51单片机DS18B20温度传感器详解:单总线时序与C51驱动实现
笔记·stm32·单片机·嵌入式硬件·51单片机
小叶肥辉15 小时前
LangChain链和LangGraph图的学习笔记【三】——分别用langchain_openai库和langchain_community库调用大模型
笔记·python·langchain
xiangyun6116 小时前
【408数据结构 08】队列:循环队列判空判满,408年年考,一次讲清
c语言·开发语言·数据结构·c++·算法
xiangyun6116 小时前
【408数据结构 06】双链表、循环链表、静态链表
c语言·开发语言·数据结构·c++·算法
从零开始的嵌入式之旅16 小时前
day44
arm开发·经验分享·笔记·嵌入式硬件
白远山17 小时前
家政服务家政社区派单实战:调度系统架构设计与实现指南
java·开发语言·小程序·架构·需求分析
Logic10118 小时前
C语言/数据结构数组题解:数字列表加一(Plus One)——进位处理与数组扩展
c语言·数据结构·模拟·数组·时间复杂度·算法题·进位
Escalating_xu18 小时前
【C语言常见概念】从第一行代码到字符串结束标志:一次打通编译、main、ASCII、转义字符与注释
c语言·c++
DevRay18 小时前
Java 21虚拟线程深度实战:告别线程池焦虑,百万并发吞吐量直接翻倍(原理+代码+避坑)
java·开发语言