c语言常见内存函数的使用与模拟实现

c语言提供了几个用于内存操作的函数,包括memcpy,memmove,memset,memcmp。这些函数可以高效且可靠的处理数据,本文将简单向大家介绍一下这些函数的使用,并简单模拟实现一下。

一、memcpy的使用

void * memcpy ( void * destination, const void * source, size_t num );

memcpy函数将source的位置向后复制num个字节的内存到destination所指向的地址并返回指向目标的指针。

在使用时,我们需要注意:

1、memcpy函数在遇见字符结束标志'\0'时不会停止。

2、当source与destination的地址有任何重复的,所复制的结果就是未定义的。

3、使用时需要引用头文件<string.h>。(后面三个函数也需要用到该头文件)

我们举个例子来使用一下memcpy函数:

#include<stdio.h>
#include<string.h>

//memcpy函数的使用

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	int* p = (int*)memcpy(arr2, arr1, sizeof(int) * 5);//sizeof计算出int类型的大小,乘以5代表复制5个int类型的内存
	int i = 0;
	while (p[i])
	{
		printf("%d", p[i]);
		i++;
	}
	return 0;
}

我们定义了整形数组arr1与arr2,通过memcpy进行5个整形的复制,由于memcpy返回的指针是void类型,所以还通过强制类型转换来转化返回指针类型,并遍历打印了arr2复制后的内容。

二、memcpy函数的模拟实现

为了更好地理解memcpy函数是使用,我们下面可以简单模拟一下该函数的实现:

void* my_memcpy(void* dest, const void* sour, size_t num)
{
	void* ret = dest;//定义一个新指针指向dest的初始地址,稍后返回ret就相当于返回了dest的初始地址
	assert(dest && sour);//判断dest与sour是否为空指针,需要引用头文件<assert.h>
	while (num--)//只复制num字节的内存,最好就是一字节一字节复制
	{
		*((char*)dest)++ = *((char*)sour)++;
	}
	return ret;
}


int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	int* p = (int*)my_memcpy(arr2, arr1, sizeof(int) * 5);//sizeof计算出int类型的大小,乘以5代表复制5个int类型的内存
	int i = 0;
	while (p[i])
	{
		printf("%d", p[i]);
		i++;
	}
	return 0;
}

我们以num作为while语句条件,这样就控制了复制的大小,随后通过指针遍历复制内存,由于空指针不能进行加减操作,我们就强制类型转换为char*(方便一字节一字节的复制)。最后返回初始的地址。

三、memmove函数的使用与模拟实现

void * memmove ( void * destination, const void * source, size_t num );

memmove函数与memcpy函数的使用极为相似,最大的区别就是memmove处理的源内存块与目标内存块可以存在重叠区域,而memcpy却不可以。

因此memmove的使用便不再多介绍,可以参考一下memcpy。

下面我们就自己写一个函数名为my_memmove来模拟实现一下memmove的功能。

#include<stdio.h>
//memmove的模拟实现:

void* my_memmove(void* dest, const void* sour, size_t num)//基本思想还是和memcpy差不多,只是多了复制重复内存块的功能
{//如何赋予代码复制重复内存块的功能,是思考的重点
	assert(dest && sour);
	void* ret = dest;
	if (dest <= sour || (char*)dest >= (char*)sour + num)//大部分机器是小端储存,栈区内存增长方向是从高地址到低地址。
		//也就是说,栈区中的新数据会被存储到先前数据的后面,栈指针会向低地址移动
		//判断是该从后向前复制还是由前向后复制
	{
		while (num--)
		{
			*((char*)dest)++ = *((char*)sour)++;//从低地址向高地址复制
		}
	}
	else
	{
		dest = (char*)dest + num - 1;//减去1方便后面num--为0跳出循环
		sour = (char*)sour + num - 1;
		while (num--)
		{
			*((char*)dest)-- = *((char*)sour)--;//由高地址向低地址复制
		}
	}
	return ret;
}

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1 + 2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

我们在my_memmove函数进行一个判断。如果目标内存区域的起始地址(dest)小于等于源内存区域的起始地址(sour),或者目标内存区域的起始地址(dest)大于等于源内存区域的结束地址(sour + num),说明两个内存区域没有重叠。

在这种情况下,我们可以直接使用while循环逐字节地从源内存区域复制数据到目标内存区域(正向复制)。循环中,每一次迭代都将源指针sour所指向的内容赋值给目标指针dest,并递增两个指针的位置,直到复制完所有的字节。

如果目标内存区域和源内存区域有重叠,我们需要保证数据不会被覆盖或失真。因此,我们通过倒序的方式进行复制,从内存区域的末尾开始(逆向复制)。

在这种情况下,我们先将目标指针dest和源指针sour都移到内存区域的末尾位置。然后使用while循环,每一次迭代都将源指针sour所指向的内容赋值给目标指针dest,并递减两个指针的位置,直到复制完所有的字节。

四、memset的使用

void * memset ( void * ptr, int value, size_t num );
memset是用来设置内存的,将内存中的值以字节为单位设置成想要的内容。它会将指针ptr所指向的内存区域前num个字节设置为value值,并返回只向ptr的指针。
使用实例如下:
#include<stdio.h>
#include<string.h>

int main()
{
	char arr[] = "hello world!";
	memset(arr, 'x', 5);
	int i = 0;
	while (arr[i])
	{
		printf("%c", arr[i]);
		i++;
	}
	printf("\n");
	int arr2[] = { 1,2,3 };
	memset(arr2, 0, sizeof(arr2));
	for ( i = 0; i < 3; i++)
	{
		printf("%d ", arr2[i]);
	}

	return 0;
}

输出情况:

xxxxx world!
0 0 0

我们通过memset函数分别给这两个数组进行参数的更改。

五、memset的模拟实现

下面是一个简单的模拟实现示例,展示了如何手动实现memset函数:

void* my_memset(void* ptr, int value, size_t num)
{
	unsigned char* p = ptr;//强制类型转化为无符号字符,是为了确保正确地处理字节的范围。
	while (num--)
	{
		*p++ = (unsigned char)value;
	}
	return ptr;
}

int main()
{
	char arr[] = "hello world!";
	my_memset(arr, 'x', 5);
	int i = 0;
	while (arr[i])
	{
		printf("%c", arr[i]);
		i++;
	}
	printf("\n");
	int arr2[] = { 1,2,3 };
	my_memset(arr2, 0, sizeof(arr2));
	for (i = 0; i < 3; i++)
	{
		printf("%d ", arr2[i]);
	}

	return 0;
}

当我们将一个整数值赋给char类型时,如果该整数值超出了有符号char类型的表示范围,可能会导致溢出和未定义行为。这也是在函数的模拟实现中选择强制类型转化为无符号字符的原因。

运行程序,结果与上面一样。

六、memcmp函数的使用

int memcmp ( const void * ptr1, const void * ptr2, size_t num );

此函数比较ptr1与ptr2的前num个字节的大小,并返回一个整数值来表较结果。

1、如果两个内存区域的内容完全相同,返回0。

2、如果第一个不匹配的字节在ptr1中的值小于ptr2中的值,返回一个负数。

3、如果第一个不匹配的字节在ptr1中的值大于ptr2中的值,返回一个正数。

以下是一个使用memcmp函数的事例:

#include<stdio.h>
#include<string.h>


int main()
{
	char arr1[] = "ABDefg";
	char arr2[] = "ABDeFG";

	int n = memcmp(arr1, arr2, sizeof(arr1));
	if (n > 0)
	{
		printf("arr1大于arr2\n");
	}
	else if (n == 0)
	{
		printf("两者相等\n");
	}
	else
	{
		printf("arr1小于arr2\n");
	}
	return 0;
}

由于第一个碰到的不同的字符是f与F,小写字符ASCII码值比较大,所以打出来的结果为:

arr1大于arr2

 七、memcmp函数的模拟实现

下面是一个简单的模拟实现示例,展示了如何手动实现memcmp函数:


int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
	const unsigned char* p1 = ptr1;
	const unsigned char* p2 = ptr2;
	for (int i = 0; i < num; i++)
	{
		if (p1[i] > p2[i])
		{
			return 1;
		}
		 if (p1[i ]< p2[i])
		{
			return-1;
		}

	}
	return 0;
}

int main()
{
	char arr1[] = "ABDefg";
	char arr2[] = "ABDeFG";

	int n = my_memcmp(arr1, arr2, sizeof(arr1));
	if (n > 0)
	{
		printf("arr1大于arr2\n");
	}
	else if (n == 0)
	{
		printf("两者相等\n");
	}
	else
	{
		printf("arr1小于arr2\n");
	}
	return 0;
}

这里使用无符号字符的原因与memset一样。在该示例中,我们使用自定义的my_memcmp函数比较两个整型数组是否相等。通过比较数组的每个元素,我们可以确定它们是否具有相同的值。

库函数的memcmp等函数自然没有这么简单,这些都只是我们从功能上的简单模拟,方便大家更好的理解函数。

  • 18
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我! 毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

渡我白衣

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

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

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

打赏作者

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

抵扣说明:

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

余额充值