22、memcpy、memset

厨子大约 4 分钟C语言基础程序程序厨

C语言中,除了mallocrealloccallocfree申请或释放内存,还有几个非常有用的函数来处理内存的拷贝和设置,其中 memcpymemsetmemmove 是最常用的几个。

本文会详细讲解这三个函数的用法、参数、返回值、适用场景和注意事项。

memcpy

memcpy 函数用于从源内存地址的起始位置开始拷贝 n 个字节到目标内存地址的起始位置。它不会处理重叠内存区域的情况,即源地址和目标地址不能有部分重叠,否则结果未定义

参数与返回值

memcpy 的函数声明如下:

void* memcpy(void* dest, const void* src, size_t n);
  • dest:目标内存地址的指针,类型为 void*
  • src:源内存地址的指针,类型为 const void*
  • n:要拷贝的字节数,类型为 size_t

返回值是目标内存地址的指针,即 dest,一般用不到。

使用场景

memcpy 常用于需要大块内存数据复制的场景,如复制结构体、数组等类型的数据。例如:

struct MyStruct {
    int a;
    float b;
    char c;
};

MyStruct src = {1, 2.3f, 'x'};
MyStruct dest;
memcpy(&dest, &src, sizeof(MyStruct));

注意事项

  • 重叠内存区域memcpy 不处理源和目标内存重叠的情况,如果重叠,需要使用 memmove
  • 目标内存大小:必须确保目标内存区域足够大以容纳要拷贝的数据,否则可能会导致内存越界。

memcpymemmove 的区别

memmovememcpy 类似, memmove 能够正确处理源和目标内存重叠的情况。因此,在处理可能重叠的内存区域时,应使用 memmove****。

memset

用于将目标内存区域的前 n 个字节设置为指定的值(通常是一个字节的值,但会被扩展到整个内存区域)。

参数与返回值

memset 的函数声明如下:

void* memset(void* s, int c, size_t n);
  • s:目标内存地址的指针,类型为 void*
  • c:要设置的值,虽然参数类型是 int,但只使用其低字节(即一个字节的值)。
  • n:要设置的字节数,类型为 size_t

返回值是目标内存地址的指针,即 s,一般用不到。

使用场景

memset 常用于初始化内存区域为特定值,主要就是将内存区域清零:

int arr[10];
memset(arr, 0, sizeof(arr));  // 将数组 arr 初始化为 0

注意事项

  • 字节值memset 设置的值是按字节设置的,如果目标类型是大于一个字节的(如 int),则整个内存区域会以该字节值进行填充,可能不会得到预期的结果。

memmove

memmove 的用法与 memcpy 几乎相同,但能够正确处理内存重叠的情况。

参数与返回值

memmove 的函数声明如下:

void* memmove(void* dest, const void* src, size_t n);
  • dest:目标内存地址的指针。
  • src:源内存地址的指针。
  • n:要拷贝的字节数。

memmove 的返回值也是目标内存地址的指针,即 dest,通常用不到。

使用场景

通常用于需要处理源和目标内存可能重叠的拷贝任务。例如:

char str[] = "1234567890";
char* src = str + 3;  // 指向 '4'
char* dest = str + 2; // 指向 '3',与 src 重叠
memmove(dest, src, 5); // 将 "45678" 拷贝到 "3" 之后的位置,得到 "124567890"

总结

  • memcpy:用于内存拷贝,不处理重叠区域,适用于大块数据拷贝。
  • memset:用于内存初始化,按字节设置值,适用于初始化内存为特定值。
  • memmove:用于内存拷贝,能处理重叠区域,是 memcpy 的安全版本。

在使用这些函数时,务必注意目标内存区域的大小和源内存区域的数据,避免内存越界和未定义行为。

练习

  1. 编写一个程序,定义两个整型数组 srcdest,其中 src 包含 5 个元素 {1, 2, 3, 4, 5},使用 memcpysrc 数组的内容复制到 dest 数组中,并打印 dest 数组的内容。
  2. 编写一个程序,定义一个整型数组 arr,包含 10 个元素,使用 memset 将数组的所有元素初始化为 0,并打印数组的内容以验证。

进阶

  1. 调研memcpy的实现原理
  2. 调研memset的实现原理
  3. 调研memmove的实现原理
  4. memcpystrcpy有什么区别?