内存函数:学习笔记7

目录

一.前言

二. memcpy模拟实现

三. memmove模拟实现

四.memcmp模拟实现


一.前言

计算机内存的实质就是以字节为编号单元的二进制序列集合,操作内存时我们应具有这样的视角。 

二. memcpy模拟实现

库函数memcpy函数首部:void *memcpy( void *dest, const void *src, size_t num );

函数的功能是将src(数据源)指向的内存空间中的前num个字节的二进制序列拷贝到dest(目的空间)指向的前num个字节的内存空间中。拷贝完成后,函数会返回目的空间的首地址。


void*类型的指针变量可以用于接收任何类型的地址。

void*类型的指针不能直接解引用,也不能直接进行运算(只能进行赋值,但不能自增或自减).若要对void*类型指针进行解引用或运算,必须对其进行强制类型转换,由于内存是以1个字节为单位的,所以大多数情况下我们一般将其强制转换为char*的指针后进行解引用和运算

(char*指针的访问权限和运算单位恰好为一个字节,符合我们进行内存操作的要求)

 

注意:对变量进行强制类型转换时,这种转换效果只是在当次对该变量解引用中生效。即对变量的强制类型转换效果只是临时的。

内存函数:学习笔记7_第1张图片

模拟实现:

内存函数:学习笔记7_第2张图片

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

注意:dest=(char*)dest + 1写成(char*)dest++是不合法的,后置++是先访问dest内存的数据作为表达式的值后再次访问dest执行++操作,所以自增操作执行时强制类型转换已经失效了,dest作为void*类型指针就不能进行自增了。

然而,dest=(char*)dest + 1写成 ++(char*)dest是可以的(但是并不推荐),因为前置++是先访问dest(类型被强制转换)完成自增,再访问其内存的数据作为表达式的值。

三. memmove模拟实现

当拷贝的目的内存空间和数据源空间有重叠部分时,若调用上面的memcpy函数在如下情况中就会出现问题。(假设我们要拷贝20个字节(num=20)的数据内容(vs2022一个整形四个字节))

内存函数:学习笔记7_第3张图片

内存函数:学习笔记7_第4张图片

很显然重叠部分的数据会丢失。

由此C标准库给出了memmove函数。

 memmove函数首部:void *memmove( void *dest, const void *src, size_t num );

函数的功能描述与memcpy基本相同,但多了一句话“If some regions of the source area and the destination overlap, memmove ensures that the original source bytes in the overlapping region are copied before being overwritten”意思是当数据源内存空间与拷贝目的空间有重叠部分时,函数可以保证重叠部分数据在被覆盖前完成拷贝。

为了实现当dest指向空间和src指向空间有重叠部分时的数据的拷贝,memmove在设计时需分以下两种情况讨论:

内存函数:学习笔记7_第5张图片

内存函数:学习笔记7_第6张图片显然区分上述两种情况的条件是指针src和dest的大小关系。

当src从数据源空间的前num个字节的最后一个字节(从后向前)开始逐个字节拷贝到目标空间的前num个字节的相应空间中。

当src>dest,进行拷贝操作时我们需要从数据源空间的前num个字节的第一个字节(从前向后)开始逐个字节拷贝到目标空间的前num个字节的相应空间中。

需要注意的是,当dest指向空间和src指向空间无重叠部分时,上述两种拷贝顺序都是可以使用的。

memmove模拟实现:

内存函数:学习笔记7_第7张图片

内存函数:学习笔记7_第8张图片

void* my_memmove(void* dest, void* src, size_t num)
{
	assert(dest && src);
	if (dest == src)
	{
		return dest;
	}
	void* ret = dest;

	if (dest > src)             //从src空间的最后一个字节开始拷贝到dest空间的前num个字节中
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
		return ret;
	}
	else                        //从src空间的第一个字节开始拷贝到dest空间的前num个字节中
	{
		while (num--)
		{
			*((char*)dest) = *((char*)src);
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
		return ret;
	}
}

四.memcmp模拟实现

memcmp函数首部:int memcmp( const void *buf1, const void *buf2, size_t num );

函数功能是,将buf1指向的内存空间中前num个字节的二进制序列与buf2指向的内存空间中前num个字节的内容进行比较。

内存函数:学习笔记7_第9张图片

memcmp函数模拟实现:

内存函数:学习笔记7_第10张图片

int my_memcmp(const void* arr1, const void* arr2,size_t num)
{
	assert(arr1 && arr2);
	while (num && *(char*)arr1 == *(char*)arr2)
	{
		arr1 = (char*)arr1 + 1;
		arr2 = (char*)arr2 + 1;
		num--;
	}
	if (num)
	{
		return *(unsigned char*)arr1 - *(unsigned char*)arr2;
	}
	else
	{
		return 0;
	}
}

注意一个细节:返回值中的强制转换类型为unsigned char*。这里不用char*的原因如下:

比如我们比较的是两个空间的4个字节的内容,两块空间中分别存放了一个整形数据,arr1中存放的整形为128,arr2中存放的整形为1,(假设机器为小端机器),在比较arr1和arr2首字节序(前八个二进制位时)如果函数返回值写成return *(char*)arr1 - *(char*)arr2;*(char*)arr1会被认为是负数(整形128的前八位二进制序列为10000000截断后作为有符号char代表-128),这时函数返回值就是一个负数,与我们预期的结果不同,返回值中的强制转换类型为unsigned char就不会出现类似的问题。

内存函数:学习笔记7_第11张图片

你可能感兴趣的:(初学者日志,学习)