【C语言】内存函数

发布于:2023-10-25 ⋅ 阅读:(92) ⋅ 点赞:(0)

1.前言

上一次学习了C语言中字符串函数有关的内容,这一次来看看内存函数。
而与上次所学不同的是这次的函数操作都是对内存。

2. memcpy使用和模拟实现

2.1 memcpy的使用

我们同样打开链接: https://legacy.cplusplus.com/看看 memcpy函数的使用。
memcpy

函数memcpysource的位置开始向后复制num个字节的数据到destination指向的内存位置。
来看一个示例:

#include <string.h>
#include <stdio.h>
int main()
{
	char arr3[] = "xxxxxxxxxx";
	char arr4[] = "yyyyyyyy";
	memcpy(arr3, arr4, 3);
	printf("%s ", arr3);

	return 0;
}

显示结果
结果
结果好像与strcpy使用的一样,但真的是一样的吗?
其实并不是,memcpy函数拷贝的是一个字节,在上面的例子中没有很好体现,我们换一个例子看看:

#include <string.h>
#include <stdio.h>
int main()
{
	int arr1[10] = { 0 };
	int arr2[] = { 1,2,3,4,5,6,7,8 };
	memcpy(arr1, arr2, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
		return 0;
}

我们调试看看,发现要拷贝20个字节,在整型中那就是传入5个整型的值,结果也是这样的。
内存改变
结果

需要注意的是:这个函数在遇到’\0’ 的时候并不会停下来。
如果sourcedestination有任何的重叠,复制的结果都是未定义的。

2.2 memcpy的模拟实现

既然知道了memcpy函数怎么使用的,那我们自己编成来实现一下。
既然需要知道传入的字节数,我们就得定义一个变量来接受。
memcpy函数是以字节为单位使用,那我们传的类型可能不同,所以要强制类型转换为char类型,这样更方面处理。
实现内存的拷贝,那肯定要把要拷贝的内存地址和拷贝到的内存地址都传入,而我们也并不知道使用者要传入什么类型的,所以使用一个void*,来接收。

下面我们来看看代码:

#include <string.h>
#include <stdio.h>
#include <assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
	void* ret = dest;
	assert(dest && src);//因为传入的地址不能为空,所以先断言一下。
	while (num--)
	{
		//拷贝一个字节
		*(char*)dest = *(char*)src;//将它们都强制类型转换为char类型
		dest = (char*)dest + 1;//拷贝一个字节完成后,继续向后拷贝
		src = (char*)src + 1;
	}
	return ret;
}

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

结果与使用memcpy函数的结果是一致的
结果
那如果要实现自己拷贝自己呢?
此时就会出现内存重叠的情况,而对于这种情况,也有所对应的内存函数,下面我们就来看看memmove

3. memmove使用和模拟实现

3.1 memmove的使用

memmove
memmove函数用来实现自身的拷贝。
拷贝
我们将1,2,3,4,5传给3,4,5,6,7的位置,那么最终实现的应该是1,2,1,2,3,4,5,8,9,10。
那是不是就是这个结果呢?
代码实现一下:

#include <string.h>
#include <stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	memmove(arr1 + 2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}

	return 0;
}

结果
结果与我们分析的是一样的。

3.2 memmove的模拟实现

我们来模拟实现一下 memmove函数的功能。
那我们就要知道它是怎么实现的。
后往前

前往后
这个函数在拷贝的时候究竟是从前往后呢,还是从后往前?
这就的视情况而定了。
在这里插入图片描述
那就需要判断destsrc的位置关系了。
如果dest < src就从前往后;如果dest在src + num之间那就要从后往前;如果destsrc + num之后,那两种都可以。

#include <string.h>
#include <stdio.h>
#include <assert.h>
void* my_memmove(void* dest, const void* src, size_t num)
{
	void* ret = dest;
	assert(dest);
	assert(src);

	//分情况讨论
	if (dest < src)
	{
		//前->后
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		//后->前
		while (num--)
		{
			//在这里num=18
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	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;
}

结果与我们用memmove函数的结果是相同的。

结果
注意:

  1. memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
  2. 如果源空间和目标空间出现重叠,就得使⽤memmove函数处理。

4. memset函数的使用

memset
memset是用来设置内存的,将内存中的值以字节为单位设置成想要的内容。
我们来看个例子:
hello world中的hello改为xxxxx world,我们需要将arr首元素地址传入,而后将要修改为x也传入,最后传入要设置是字节数量。
我们来看看代码及结果:

#include <string.h>
#include <stdio.h>
int main()
{
	char arr[] = "hello world"memset(arr, 'x', 5);
	printf("%s\n", arr);

	return 0;
}

结果
结果显然是所分析的那样。
注意:memset是以字节为单位设置内存值的。

5. memcmp函数的使用

memcmp
比较从ptr1和ptr2指针指向的位置开始,向后的num个字节。

返回值如下:
结果
strcmp不同的是 memcmp函数的使用是以字节为单位的。
下面来看一个示例:

#include <string.h>
#include <stdio.h>
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abgfqwertyuiop";
	int ret = memcmp(arr1, arr2, 2);
	printf("%d\n", ret);

	return 0;
}

0
比较的是前两个字节,而他们两个前两个字节都是相同的,所以返回值就为0。

那当我们不使用char类型来比较时,会是什么情况呢?

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

	return 0;
}

这里结果为0,那是为什么呢?
在这里插入图片描述
我们调试,观察内存,我们传入比价的是前5个字节,而在内存发现它们是相同的,结果也当然返回的是0。
结果

有问题请指出,让我们一起共同进步吧!

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到