【C语言】内存操作函数详解:memcpy、memmove与memset的原理与实现
介绍c语言中memcpy函数和memmove函数的使用与模拟实现
memcpy
概念
memcpy
是 C 标准库中的一个函数,用于在内存中复制一定数量的字节。它的声明在 <string.h>
头文件中,功能是将源内存区域中的数据复制到目标内存区域。这个函数通常用于处理内存块的复制操作。
函数原型
void *memcpy(void *dest, const void *src, size_t n);
参数
dest
:指向目标内存区域的指针,数据将被复制到这个位置。src
:指向源内存区域的指针,从这个位置的数据将被复制到目标位置。n
:要复制的字节数。
返回值
- 返回指向目标内存区域(
dest
)的指针。
使用说明memcpy
会将从 src
开始的 n
个字节复制到 dest
。它不会检查目标区域是否足够大,也不会进行内存重叠的处理,如果 src
和 dest
内存区域重叠,行为是未定义的(通常会导致数据损坏)。
注意事项
- 内存重叠:
memcpy
不适合用于源和目标内存区域重叠的情况。如果源和目标重叠,应该使用memmove
函数,它会正确处理重叠内存区域。 - 效率:
memcpy
通常较为高效,因为它是专门为内存块复制设计的,而且许多编译器或系统提供对其优化的实现。 - 边界检查:
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
的效率比较高,它通常是通过直接操作内存来实现的,特别适合于初始化大数组或清零内存区域。
更多推荐
所有评论(0)