memcpy

概念

memcpy 是 C 标准库中的一个函数,用于在内存中复制一定数量的字节。它的声明在 <string.h> 头文件中,功能是将源内存区域中的数据复制到目标内存区域。这个函数通常用于处理内存块的复制操作。

函数原型

void *memcpy(void *dest, const void *src, size_t n);

参数

  • dest:指向目标内存区域的指针,数据将被复制到这个位置。
  • src:指向源内存区域的指针,从这个位置的数据将被复制到目标位置。
  • n:要复制的字节数。

返回值

  • 返回指向目标内存区域(dest)的指针。

使用说明
memcpy 会将从 src 开始的 n 个字节复制到 dest。它不会检查目标区域是否足够大,也不会进行内存重叠的处理,如果 srcdest 内存区域重叠,行为是未定义的(通常会导致数据损坏)。

注意事项

  1. 内存重叠memcpy 不适合用于源和目标内存区域重叠的情况。如果源和目标重叠,应该使用 memmove 函数,它会正确处理重叠内存区域。
  2. 效率memcpy 通常较为高效,因为它是专门为内存块复制设计的,而且许多编译器或系统提供对其优化的实现。
  3. 边界检查memcpy 不会检查目标内存区域是否足够大,使用时需要确保目标区域足够容纳要复制的数据。

memcpy 是一个非常常用的低级内存操作函数,适用于复制大块内存数据。需要注意其内存重叠问题,并且确保目标区域足够大。

使用场景:

void test1()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	//memcpy - 将 arr1 的前五个数据拷贝给 arr2
	memcpy(arr2, arr1, 20);

	float arr3[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
	float arr4[5] = { 0 };
	//把 arr3 中的前 5 个数据拷贝到 arr4 中
	memcpy(arr4, arr3, 12);
}

在这里插入图片描述


模拟实现:

由于实现代码并不知道使用者会传入什么类型参数,所以使用 void * 类型,并用字节为单位。

void* my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	// 存 dest 的起始位置
	void* ret = dest;
	while (num--)
	{
		// 单位是字节,使用 char* 强转(大小为 1 字节)
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1; // 向后一字节
		src = (char*)src + 1;
	}
	return ret;
}

问题

当我们想将数组的一定范围内的元素拷贝到另一部分时,使用 memcpy 将会出现问题。

void test2()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	// 将 arr1 中 1 2 3 4 5 拷贝到 3 4 5 6 7 的位置
	my_memcpy(arr1 + 2, arr1, 20);
}

在这里插入图片描述

如上图所示,输出结果并不是期待的 "1 2 1 2 3 4 5 8 9 10"

原因是因为当把 1 2 拷贝到 3 4 位置时,此时序列已然变成了 "1 2 1 2 5 6 7 8 9 10"

此时继续拷贝,只会继续将 1 2 向后拷贝。

因此 C 语言提供了解决此类问题的 memmove 函数。


memmove

在这里插入图片描述

在功能上 memmove 依然是将 src 中的字符拷贝给 dest,但在处理内存重叠方面更为优秀。

下面是其使用:

void test2()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	// 将 arr1 中 1 2 3 4 5 拷贝到 3 4 5 6 7 的位置
	// my_memcpy(arr1 + 2, arr1, 20);
	memmove(arr1 + 2, arr1, 20);
}

在这里插入图片描述


在同样的场景下,memmove 可以实现重叠内存的字符拷贝。

模拟实现思路:

在这里插入图片描述

因此我们有两个选择:

在这里插入图片描述


选择 B 进行 memmove 的模拟实现:

void my_memmove(void* dest, void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	// 情况①
	if (dest < src)
	{
		// 前->后
		while (num--) // 即模拟实现 memcpy 的函数代码
		{
			*(char*)dest = *(char*)src;
			dest = *(char*)dest + 1;
			src = *(char*)src + 1;
		}
	}
	else
	{
		// 后->前
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num); // 每次 num--,实现后->前拷贝
		}
	}
	return ret;
}

再次进行内存重叠拷贝时:

void test2()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	// 将 arr1 中 1 2 3 4 5 拷贝到 3 4 5 6 7 的位置
	// my_memcpy(arr1 + 2, arr1, 20);
	// memmove(arr1 + 2, arr1, 20);
	// memmove(arr1 + 6, arr1, 20); // 越界
	my_memmove(arr1 + 2, arr1, 20);
}

在这里插入图片描述


memset

memset 用于将一块内存区域设置为某个特定的值,通常用于初始化数组或清零。

memset 使用示例

void test3()
{
	int arr[10] = {0};
	// 将 arr 中的每个元素都设置为 1
	memset(arr, 1, sizeof(arr));
	for (int i = 0; i < 10; ++i)
	{
		printf("%d ", arr[i]); // 输出 1 1 1 1 1 1 1 1 1 1
	}
}

模拟实现:

void* my_memset(void* dest, int value, size_t num)
{
	assert(dest);
	unsigned char* p = dest;
	while (num--)
	{
		*p = (unsigned char)value; // 将每个字节都设置为指定值
		p++;
	}
	return dest;
}

memset 工作原理

memset 实际上是通过字节的方式将指定的值填充到指定的内存区域。由于 memset 的效率比较高,它通常是通过直接操作内存来实现的,特别适合于初始化大数组或清零内存区域。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐