赶紧来修炼内功发~内存函数详解大全-memcpy、memmove、memcmp

本文详细介绍了C语言中memcpy、memmove、memcmp和memset四个内存操作函数的使用方法及注意事项,并提供了模拟实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1. memcpy

EX:

 PS:

模拟实现:

2. memmove

EX:

​编辑

模拟实现:

3. memcmp

EX:

PS:

 模拟实现:

 4. memset

EX: 

模拟实现


1. memcpy

 memcpy函数的作用为:将source指向的地址拷贝num个字节到destination指向的的地址

(注意!这里时拷贝num个字节!)

返回类型为void*类型,返回destination指向的起始地址

前两个参数均为void*类型,

第一个参数为拷贝的目的地的起始地址;

第二个参数为要拷贝的内容所在的起始地址;

第三个参数为size_t类型,即无符号整型类型,num为要拷贝的字节数;

与strnmpy函数差不多,只不过memcpy函数可以拷贝任意类型的数据。

EX:

#include <stdio.h>
#include <string.h>
int main()
{
	int arr1[15] = { 0 };
	int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
	memcpy(arr1, arr2, 20);

	return 0;
}

 该样例是将arr2指向的地址的前20个字节的内容拷贝到arr1指向的地址中,因为这些数据的类型均为int类型,一个int类型占4个字节,拷贝20个字节就是拷贝5个int类型的数据。

#include <stdio.h>
#include <string.h>
int main()
{

	float arr1[15] = { 0 };
	float arr2[] = { 1.0f,2.0f,3.0f,4.0f,5.0f,6.0f,7.0f };
	memcpy(arr1, arr2, 20);

	return 0;
}

 该样例是拷贝float类型数据,同理,拷贝20个字节的内容就是拷贝5个float类型的数据。

 PS:

该函数遇到'\0'也不会停止,直到拷贝完规定的字节为止~

#include <stdio.h>
#include <string.h>
int main()
{
	int arr1[15] = { 0 };
	int arr2[] = { 1,2,3 };
	memcpy(arr1, arr2, 20);

	return 0;
}

 

 arr2中只有3个int类型的数据,可是memcpy函数要拷贝20个字节的数据,

通过监视我们可以看到,拷贝完3个int类型的数据之后,不管后面是不是'\0',只要没有拷贝完规定的字节数,都会往后继续拷贝~

模拟实现:

创建一个my_memcpy函数,模拟实现memcpy函数功能: 

void* my_memcpy(void* dest, const void* sur, size_t num)//返回值类型参数类型与原函数保持一致
{//dest所指向的字符串需要被修改,不用const保护,src所指向的字符串不需要被修改用const修饰保护
	assert(dest && sur);//断言一下,保证两个指针均不为空指针,若为空指针则中止程序并报错
	void* ret = dest;//创建一个char*变量保存起始地址,后面需要返回起始地址
	while (num--)//控制拷贝几个字节
	{
		*(char*)dest = *(char*)sur;//char类型为一个字节,强制类型转化为char类型就可以一个节一个字节拷贝
		(char*)dest += 1;          //强制类型转化为char*再+1,就跳过一个字节大小
		(char*)sur += 1;           //同理
	}
	return ret;     //返回所保存的起始地址
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "qwer";
	my_memcpy(arr1, arr2, 4);
	printf("%s\n", arr1);
	return 0;
}

该函数不能拷贝重合的情况!

2. memmove

memmove函数的作用:与memcpy函数的作用相同~

和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。

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

EX:

int main()
{
	int arr[15] = { 1,2,3,4,5,6,7,8,9,10 };
	memmove(arr + 2, arr, 20);

	return 0;
}

 该样例就十分直观了,将arr内存块的内容拷贝20个字节到arr+2的内存块,

显然有重叠的部分,但是memmove函数可以完美解决重叠的问题~

模拟实现:

创建一个my_memmove函数,模拟实现memmove函数功能: 

void* my_memmove(void* dest, const void* sur, size_t num)
{
	assert(dest && sur);
	void* ret = dest;     //以上代码与前几个模拟函数类似,这里就不多赘述了
	if (dest < sur)
	{
		while (num--)
		{
			*(char*)dest = *(char*)sur;  //强制类型转化为char*类型,因为char类型就是占用一个字节
			dest = (char*)dest + 1;
			sur = (char*)sur + 1;
		}
	}
	else
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)sur + num);//这里不用再-1了,while判断之后就自动-1了。
		}
	}
	return ret;
}
int main()
{
	char arr1[] = "abcdef";
	my_memmove(arr1 + 2, arr1, 4);
	printf("%s\n", arr1);
	return 0;
}

这里只解释拷贝重合的情况:(其余都与my_memcpy函数类似)

第一种情况:src在dest前面

若从前向后拷贝那么重合的部分就会被提前更改,导致后面拷贝的内容是已经被更改完的,结果错误 

所以,这种情况我们需要从后向前拷贝,先拷贝重叠部分,之后被覆盖的时候就没有影响了

第二种情况:src在dest后面

 这种情况也是先拷贝重叠部分,即从前向后拷贝,之后重叠部分被覆盖的时候也是没有影响的

3. memcmp

memcmp函数的作用:比较ptr1与ptr2内存块的num个字节的内容~

前两个参数就是要比较的内存块的地址;

第三个参数就是要比较的字节数。 

返回值为int类型:

EX:

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,0,0 };
	int ret = memcmp(arr1, arr2, 12);

	printf("%d\n", ret);

	return 0;
}

 前12个字节的内容都一样,所以返回0

PS:

要注意的是:

这里比较的以字节为单位的,所以是要牵扯到大小端存储的~

不了解大小端存储的可以看这篇文章:大小端存储解析

以上面的例题为例:

小端存储:

 大端存储:

比较12个字节是一样的,但是比较13个字节就会发现结果不一样了! 

小端存储:

最后一个字节arr1中为04;arr2中为00;

arr1比arr2大~ 

大端存储:

最后一个字节arr1中为00;arr2中也为00;

arr1与arr2一样大~ 

 模拟实现:

创建一个my_memcmp函数,模拟实现memcmp函数的功能:

int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
	assert(ptr1 && ptr2);   //以上代码与前面的模拟函数类似,这里就不多赘述了
	while (num--)
	{
		if (*(char*)ptr1 == *(char*)ptr2)
		{
			(char*)ptr1 += 1;
			(char*)ptr2 += 1;
		}
		else
			return *(char*)ptr1 - *(char*)ptr2;
	}
	return 0;
}
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,0,0 };
	int ret = my_memcmp(arr1, arr2, 12);

	printf("%d\n", ret);

	return 0;
}

该函数与我这个系列的(二)号文章中的strncmp函数模拟原理几乎相同,只不过多了个强制类型转换,也不用判断'\0'

有兴趣的可以看看那篇文章:strn...类字符串函数详解及模拟实现 

该函数也是分大小端区别的! 

 4. memset

memset函数的作用:修改内存信息

第一个参数是要修改的内存的起始位置;

第二个参数是要修改成为的内容;

第三个参数是要修改的字节数; 

EX: 

int main()
{
	int arr[] = { 1,2,3,4 };
	memset(arr, 1, 8);
	
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

  

 通过观察内存我们可以看到将arr起始的后八个字节的内容都改成了1

模拟实现:

创建一个my_memset函数,模拟实现memset函数的功能:

void* my_memset(void* ptr, int value, size_t num)
{
	assert(ptr);
	while (num--)  //控制更改几个字节的内容
	{
		*(char*)ptr = (char)value;  //强制类型转化为char*与char类型进行赋值,因为char类型占一个字节
		(char*)ptr += 1;     //强制类型转化为char*类型,+1,跳过一个字节的大小
	}
}
int main()
{
	int arr[] = { 1,2,3,4 };
	my_memset(arr, 1, 8);

	int i = 0;
	for (i = 0; i < 4; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蒲公英的吴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值