C/C++内存函数功能介绍与模拟实现

  在日常代码编写中,我们往往都是基于已经开发好的编译器及平台进行代码的编译与功能的实现,而编译器所自带的库函数可以在我们编写过程中起到很好的辅助作用,通过使用这些库函数可以直接实现代码所需要的一部分功能,从而大大提升我们的编写效率,节约代码的编写时间。而不同种类的库函数有着不同的功能与应用场景,例如最常用的scanf(输入)、printf(打印)、sizeof(计算字节长度)等等...而本文主要围绕库函数一些好用又易懂的内存函数进行叙述和介绍。

目录

1、memcpy

2.memmove

3.memcmp

4.memset


1、memcpy

库函数:void * memcpy ( void * destination, const void * source, size_t num );

标准规定:

1.函数memcpysource的位置开始向后复制num字节的数据到destination的内存位置。

2.这个函数在遇到 '\0' 的时候并不会停下来。

3.如果sourcedestination有任何的重叠,复制的结果都是未定义的。

4.头文件#include<string.h>

memcpy作为内存拷贝函数,和strcpy(字符串拷贝)是有很大区别的,strcpy只限于char类型,其返回类型和传参数据类型都是char相关类型(详细介绍可查看C/C++字符串函数功能介绍与实现)而memcpy的返回类型和参数类型都是void型,适用于任意 类型的数据拷贝。

 如上面的代码所示,我们可以使用memcpy吧int型的arr1数组中的内容拷贝到arr2中。

模拟实现:

void* my_memcpy(void* dest,const void* src,size_t num)
{
	while (num--)
	{
		*(char*)dest = *(char*)src;
		dest=(char*)dest + 1;
		src = (char*)src + 1;
	}
}

因为在传参时使用的是void*而num又是字节数,考虑到memcpy适合任意类型的数据,所以在拷贝数据时,按照一个一个字节来拷贝,所以需要将void*强制转换成char*类型然后进行解引用操作以达到拷贝的目的。

而当我们想在同一个数组内部进行重叠拷贝时,却出现了以下的状况:

 原本按我们的推演,拷贝完成后应该是12123458910,但结果却不一样,因为memcpy在进行拷贝的时候,数组中的内存发生了动态覆盖,我们先将12拷贝到34的位置,然后要 将34拷贝到56的位置,但当程序走到要拷贝34时,34已经被刚刚的12覆盖了,依次往后推,拷贝的就都是12了。

所以memcpy是用来处理不相关的/不重叠的内存拷贝的。

2.memmove

库函数:void * memmove ( void * destination, const void * source, size_t num );

标准规定:

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

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

从参数上看,memmove和memcpy一模一样,它们的用法也是一模一样,但在功能上却是有所差异,memmove具有重叠内存拷贝的功能,也就是说它可以处理重叠拷贝这种情况。

 模拟实现:

要想实现重叠拷贝的功能,拿数组举例,在同一数组中进行拷贝有两种情况,目标地址在需要被拷贝的地址前面,或者后面。

此时就需要分成三种情况进行讨论:

1.如果目标地址在前

此时如果要做到成功拷贝,做到后面未拷贝的数据不被覆盖,从前往后拷贝显然是最好的方式,先将3拷贝到2的位置,然后将4拷贝到3的位置,然后将5拷贝到4的位置...如此从后向前进行拷贝,就不会发生还未拷贝的数据被覆盖。

2.目标地址在后

 当目标地址在被拷贝的数据之后时,如果再从前往后拷贝,把3放到6的位置把4放到7的位置,可是当拷贝到6 7时,6 7已经被3 4覆盖了,所以此时就应该采用从后往前的拷贝方式,先把7放到10的位置,再把6放到9的位置,再把5放到8的位置,这样依次拷贝,就不会存在覆盖问题了。

3.dest和src没有重叠的部分

当src和dest没有重叠的部分时,向前向后拷贝都可以。

所以我们可以这样写,当dest<src时,从前向后拷贝,反之,从后向前。

void* my_memmove(void* dest, void* src, size_t num)
{
	void* ret = dest;
	assert(dest && src);
	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 ret;
}

而在我们日常测试中,我们可以发现,在vs编译器下memcpy也可以被用来进行重叠拷贝,但是在其他的编译器当中,这是不一定的,所以在日常代码编写中,考虑到跨平台的使用,我们还是要按照C99规定来进行代码编写。

3.memcmp

库函数:int memcmp ( const void * ptr1, const void * ptr2, size_t num );

标准规定:

1.比较从ptr1ptr2指针开始的num个字节

比较两个地址往后num个字节一个一个的比较,根据标准规定可以看到,如果 如果第一个小于第二个返回小于0的数,如果大于返回大于0的数,如果等于则返回0。

4.memset

库函数:void* memset(void* ptr,int value,size_t num);

填充内存块,也就是设置内存,把ptr所指向的前num个字节的内容设置成value值,一个一个字节进行设置,它是以字节为单位进行设置。

 通过上面可以看到,通过memset成功的将前10个字节改成了01。

  以上就是我们日常代码编写中比较常见,实用性比较高的一些内存函数,我们通过对其参数、返回值、模拟实现的研究增强了对库函数的理解和认知,而熟练掌握库函数的使用会大大减少我们的工作量,提高代码运行效率的同时也增强了代码的可读性,使其更加凝练更加简介。

  本章内容就到此结束了,每一篇文章都是博主的精心打磨,耐心编排。更多好文关注博CSDN。一键三连不迷路。

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

C+五条

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

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

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

打赏作者

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

抵扣说明:

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

余额充值