前言

本篇博文是关于C语言中内存函数的相关内容、使用及其模拟实现,欢迎各位友友三连学习哦!

一、memcpy使用和模拟实现

1. memcpy函数的使用

mem--memory--记忆,(在计算机中)内存

与strcpy相比,memcpy是针对内存块进行的拷贝 

1. 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置(右边的数据拷贝到左边来)

2. 函数遇到'\0'不会停下来

3. 如果source和destination有任何的重叠,复制的结果都是未定义的

4. 返回值:memcpy拷贝结束后,返回的是目标空间的起始地址,而且是void*类型(实现各种类型数据的拷贝)

5. 使用该函数,需要引用头文件:string.h

2. memcpy函数的模拟实现

 ·使用void*类型的指针,是为了能够进行各种类型数据的拷贝

·将void*类型的指针强制类型转换成char*类型,一次解引用访问一个字节;但是转换成int*类型就会不方便:若拷贝的数据是7(奇数)个字节,则int*类型的指针解引用访问4个字节,不能刚好记就拷贝完所有数据。

void* my_memcpy(void* dest, const void* src, size_t num)//void*类型的指针:可以用来接收各种类型的地址
{
	void* ret = dest;//备份dest的初始值
	int i = 0;
	//指针解引用:assert断言
	assert(src && dest);
	while (num--)
	{
		*(char*)dest = *(char*)src;
		src = (char*)src + 1;
		dest = (char*)dest + 1;//也可以写成:(char*)dest++;
		//            (char*)src++;
//dest和 src不再指向初始地址
	}
	//返回目标空间的起始地址
	return ret;
}

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	my_memcpy(arr2, arr1, 20);//5个字节(以字节为单位)

	int i = 0;
	for (i = 0; i < 20; i++)
	{
		printf("%d ", arr2[i]);
	}

	return 0;
}

那针对上面的代码,能否实现将arr1中的1、2、3、4、5拷贝到arr1中的4、5、6、7、8中呢?

void* my_memcpy(void* dest,const void* src,size_t num)//void*类型的指针:可以用来接收各种类型的地址
{
	void* ret = dest;//备份dest的初始值
	int i = 0;
	//指针解引用:assert断言
	assert(src && dest);
	while (num--)
	{
		*(char*)dest = *(char*)src;
		src = (char*)src + 1;
		dest = (char*)dest + 1;//也可以写成:(char*)dest++;
		                       //            (char*)src++;

	}
	//返回目标空间的起始地址
	return ret;
}

//1 2 3 4 5 (源空间)拷贝放在 3 4 5 6 7(目标空间)
int main()
{
	//strcpy--字符串的拷贝
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	//一边拷贝,源空间一边被改

	my_memcpy(arr1+2, arr1, 20);
	//memcpy(arr1 + 2, arr1, 20);
	//结果不一样:因为memcpy完成不重叠的拷贝

	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}

	return 0;
}

 运行结果:

对该运行结果的分析过程:

 

memcpy函数的运行结果:

可以发现两个结果不一样,因此,记住,不能期望memcpy函数能够实现重叠内存块的拷贝。

既然这样,那内存块重叠的拷贝应该如何实现呢?这就引出了memmove函数。

二、 memmove函数的使用和模拟实现

2.1 memmove函数的使用 

memmove函数的使用方式几乎和memcpy函数一样。

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	memmove(arr + 2, arr, 5 * sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}


	return 0;
}

运行结果:

2.2 memmove函数的模拟实现

如果我们想将一个数组{1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10}中的 1,2 ,3,4,5拷贝到该数组中的3,4,5,6,7的位置,应该如何进行拷贝呢?,根据下图,我们的预期结果就是:输出1 2 1 2 3 4 5 8 9 10.

在前面的一张图中,我们就分析了一种办法,就是将需要拷贝的元素一个一个地从前向后拷贝,但是这种办法不能达到我们预期想要的结果(1 2 1 2 1 2 1 8 9 10)(也就是该方法从前向后仅适用于部分情况,如:该数组的3 4 5 6 7拷贝到1 2 3 4 5中去,就可以从前向后拷贝)。还有两种办法:法一:添加一个备份数组,在备份数组中完成拷贝后将改数字的数据放回源数组。

但是该方法缺点就是:添加了一个备份数组,浪费了空间资源,所以选择方法二。

法二:将需要进行拷贝的数据进行倒着拷贝,分析过程如下图:

 这样,就不会出现之前的数据一边拷贝一边被修改的情况了。

但是,我们应该如何选择数据是从前向后拷贝还是从后向前拷贝呢?

选择方法一区分就比较简单。对于34567拷贝在12345的memmove函数模拟实现的代码如下:得到的就是预期的结果

// //memmove函数拷贝完成之后会返回目标空间的起始地址
void* my_memmove(void* dest, const void* src, size_t num) 
{
	assert(dest && src);
	void* ret = dest;
	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;
}

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

	return 0;
}

三、memset函数的使用和模拟实现

 3.1 memset函数的使用

1. ptr:指向需要被填充的内存块;

2. value:需要设置的值;

3. num:字节个数(以字节为单位,不能以元素类型为单位进行设置);

4. 使用该函数需要引用头文件string.h 

4. 作用:以字节为单位,把ptr指向的位置的数据设置成num个value。例如:

注意:memset是以字节为单位对值进行设置的,而不是以元素为单位设置的(设置的不是元素)

对于下面的代码:

#include <string.h>
#include <stdio.h>
int main()
{
	int arr[5] = { 0 };//
	memset(arr, 1, 20);//20个字节(以字节为单位):从arr数组的第一个元素开始,往后将每一个元素设置为1,一共设置20个字节
	for (int i = 0; i < 5; i++)
	{
		//打印这5个元素
		printf("%d ", arr[i]);
	}

	return 0;
}

我么预期结果是输出:1 1 1 1 1,但是现在输出的却是:

利用调试来分析此结果出现的原因:

十六进制的1010101转换成二进制,就是16843009,所以输出了5个16843009。

如果是改成0的话,就可以使用此方法,使得每一个字节都变成0,结果就是0

四、memcmp函数的使用及模拟实现

1. 从ptr1和ptr2指针指向的位置开始,向后访问num个字节的内容,进行比较; 

2. 针对内存块的比较

3. 返回值:

 例如:

#include <string.h>
#include <stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7 };
	int arr2[] = { 1,2,3,4,8,8,8 };
	int ret = memcmp(arr1, arr2, 16);
	printf("%d\n", ret);

	return 0;
}

 进行一个字节一个字节的比较,比了 16个字节,结果还是一样大,所以返回0。


以上就是内存函数相关的全部分享啦!友友的支持会是我前进的动力哦!

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐