C语言零基础教程(memset,memcpy函数,memmove函数)

文章介绍了C语言中用于内存操作的三个关键函数:memset用于设置内存区域的值,memcpy用于无重叠的内存拷贝,而memmove能处理可能的内存区域重叠问题。文中详细阐述了每个函数的使用方法、参数、工作原理以及注意事项,并通过示例代码展示了它们的正确用法和潜在陷阱。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文章已经生成可运行项目,


前言

本篇文章来讲解一下memset和memcpy函数,这两个函数在C语言中也是比较重要的,这里我们就来学习一下这两个函数的使用方法吧。

一、memset函数

memset 函数是一个C标准库中的函数,用于将一块内存区域的每个字节设置为指定的值。

memset 的定义如下:

void *memset(void *ptr, int value, size_t num);

函数的参数包括 ptr,表示要设置的内存区域的起始地址;value,表示要设置的值,通常以整数表示,但在传给 memset 时会自动转换为 unsigned char 类型;num,表示要设置的字节数。

memset 函数的工作原理是将指定值 value 拷贝到指定内存区域 ptr 所指向的每个字节中,重复拷贝 num 次。

常见的用法是将内存区域初始化为特定值,例如将整个数组清零:

int arr[10];
memset(arr, 0, sizeof(arr));

上述代码将数组 arr 的所有元素设置为零。这是非常高效的一种方式,特别是对于大型数组或者结构体,因为它直接操作内存,而不是逐个元素赋值。

需要注意的是,memset 函数只能设置每个字节的值,因此对于非 char 型的数组,设置的值可能会被截断或产生不可预测的结果。针对非字符类型的数组或结构体,应该使用其他方法来进行赋值。

此外,还需要谨慎使用 memset,因为它没有边界检查,容易导致越界操作或者非法访问内存。

易错点:

当使用 memset 函数给不同类型的数组置为某个值时,确实需要注意不同类型的字节大小和表示范围,以避免出现问题。以下是一个示例:

#include <stdio.h>
#include <string.h>

int main() {
  int intArray[5];
  char charArray[5];

  // 设置 int 类型数组为 1
  memset(intArray, 1, sizeof(intArray));

  // 设置 char 类型数组为 1
  memset(charArray, 1, sizeof(charArray));

  printf("intArray:\n");
  for (int i = 0; i < 5; i++) {
    printf("%d ", intArray[i]);
  }

  printf("\n\ncharArray:\n");
  for (int i = 0; i < 5; i++) {
    printf("%d ", charArray[i]);
  }

  return 0;
}

运行上述代码,输出如下:

intArray:
16843009 16843009 16843009 16843009 16843009

charArray:
1 1 1 1 1

可以看到,memset 函数对 int 类型数组的每个字节都设置为 1,并没有按预期将整个 int 类型的元素设置为 1。这是因为 memset 函数按字节拷贝,将 1(int 类型转换为 unsigned char 类型)复制到了每个字节,并没有设置整个 int 类型元素的值。

相比之下,对 char 类型数组使用 memset 函数,每个字节都被设置为 1,包括 ASCII 值为 1 的字符。

因此,在使用 memset 函数时,应注意被设置的值要与数组元素类型相匹配,以避免产生意料之外的结果。如果想将整个 int 类型数组设置为某个值,可以使用循环逐个赋值的方式来确保正确设置。

二、memcpy函数

memcpy 函数是 C 标准库中的一个函数,用于在内存之间进行字节级别的数据拷贝。memcpy 可以将源内存区域的内容复制到目标内存区域,并返回指向目标内存区域的指针。

memcpy 的定义如下:

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

函数的参数包括 dest,表示目标内存区域的起始地址;src,表示源内存区域的起始地址;n,表示要复制的字节数。

memcpy 函数会将源内存区域中的 n 个字节的数据复制到目标内存区域,可能包含原先的内容。函数不会检查边界,因此保证源和目标内存区域的大小至少为 n 是非常重要的。

以下是一个示例,展示 memcpy 的用法:

#include <stdio.h>
#include <string.h>

int main() {
    char src[] = "Hello, world!";
    char dest[20];

    memcpy(dest, src, strlen(src) + 1);

    printf("Copied string: %s\n", dest);

    return 0;
}

上述代码将源字符串 src 复制到目标字符数组 dest 中。memcpy 函数使用了 strlen(src) + 1 作为要复制的字节数,确保整个字符串被复制到目标数组中,包括字符串的结尾符 ‘\0’。

在运行代码后,输出如下:

Copied string: Hello, world!

可以看到,源字符串 src 成功地复制到了目标字符数组 dest 中。

需要注意的是,memcpy 函数在进行内存拷贝时是按字节级别操作的,不关心内存中保存的是什么类型的数据。这也意味着在使用 memcpy 时,应确保源和目标内存区域之间没有重叠,以免产生意想不到的结果。如果源和目标内存区域有重叠,可以使用 memmove 函数来避免数据被破坏。

三、memmove函数

memmove 函数是一个 C 标准库中的函数,用于在内存之间进行字节级别的数据拷贝。与 memcpy 函数不同的是,memmove 函数可以处理可能发生重叠的内存区域的拷贝。

memmove 的定义如下:

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

函数的参数包括 dest,表示目标内存区域的起始地址;src,表示源内存区域的起始地址;n,表示要复制的字节数。

memmove 函数将会将源内存区域中的 n 个字节的数据复制到目标内存区域中,即使源和目标内存区域有部分或完全重叠。函数会自动处理重叠情况,以确保数据被正确复制。

以下是一个示例,展示 memmove 的用法:

#include <stdio.h>
#include <string.h>

int main() {
  char str[] = "Hello, world!";
  memmove(str + 7, str, strlen(str) + 1);

  printf("Moved string: %s\n", str);

  return 0;
}

上述代码将字符串 str 移动了 7 个位置,即将字符串的前部分移动到后部分。在这个例子中,memmove 函数被用来处理源和目标内存区域可能重叠的情况。

运行代码后,输出如下:

Moved string: world! Hello,

可以看到,源字符串 str 成功地移动了 7 个位置,并且重叠部分的数据也被正确处理。

需要注意的是,相比于 memcpy 函数,memmove 函数的实现可能会更加复杂和耗时,因为需要处理内存区域的重叠情况。因此,在没有重叠的情况下,推荐使用 memcpy 函数来进行拷贝操作,因为它的实现更简单且通常更高效。只有当存在内存区域重叠的情况时,才需要使用 memmove 函数

总结

本篇文章就讲解到这里,大家看完后可以进行实验验证。

本文章已经生成可运行项目
05-10
### 关于 `memset` 函数的使用 #### 函数定义 在 C 和 C++ 中,`memset` 是一个标准库函数,用于将一块连续的内存区域填充为指定的值。它的声明位于头文件 `<cstring>` 或者 `<string.h>` 中。 ```cpp #include <cstring> // 或 <string.h> void* memset(void* dest, int value, size_t count); ``` - **参数说明** - `dest`: 要操作的目标内存地址。 - `value`: 填充到目标内存中的值,该值会被转换成无符号字符(unsigned char)后再存储。 - `count`: 需要填充的字节数量。 - **返回值**: 返回指向已修改内存块的第一个字节的指针[^1]。 --- #### 使用场景与示例 ##### 初始化数组 可以利用 `memset` 将整个数组的内容设置为特定值,最常见的是将其置零。 ```cpp #include <iostream> #include <cstring> int main() { int a[5] = {1, 2, 3, 4, 5}; // 将数组 a 的每个字节替换为 0 memset(a, 0, sizeof(a)); for (int i = 0; i < 5; i++) { std::cout << a[i] << " "; } } // 输出: 0 0 0 0 0 ``` 此代码片段展示了如何通过调用 `memset` 来清除整数数组的所有元素至零状态[^2]。 --- ##### 结构体初始化 对于复杂的数据结构如自定义结构体,也可以借助 `memset` 实现快速初始化。 ```cpp typedef struct { int a; float b; double c; } TEST; int main() { TEST test; // 设置结构体内存全为 0 memset(&test, 0, sizeof(TEST)); std::cout << test.a << "\n"; // 输出: 0 std::cout << test.b << "\n"; // 输出: 0.000000 std::cout << test.c << "\n"; // 输出: 0.000000 return 0; } ``` 需要注意的一点是,如果结构体成员包含指针或其他动态分配的对象,则简单地应用 `memset` 可能不会达到预期效果,甚至可能引发未定义行为[^4]。 --- ##### 动态内存初始化 当涉及堆上的数据管理时,同样适用 `memset` 方法来完成必要的预处理步骤。 ```cpp #include <cstdlib> #include <cstdio> int main() { int* temp = (int*)malloc(5 * sizeof(int)); // 开辟五个 int 类型的空间 if (!temp) return -1; // 检查 malloc 是否成功 // 利用 memset 把这些空间内的所有位都设为 0 memset(temp, 0, 5 * sizeof(int)); for (int i = 0; i < 5; ++i){ printf("%d ", temp[i]); } free(temp); // 记得释放资源 return 0; } // 输出结果:0 0 0 0 0 ``` 这里强调一点,尽管可以通过 `memset` 给任意大小的缓冲区赋固定数值,但由于其逐字节操作特性,在某些情况下可能会遇到意想不到的结果,比如尝试设定负数或者浮点类型的特殊值时应格外小心[^4]。 --- ### 注意事项 虽然 `memset` 提供了一种简便的方式来进行批量赋值,但在实际开发过程中需谨慎对待以下几个方面: 1. 数据类型匹配问题; 2. 不适合直接应用于含有非平凡构造器/析构器的类实例; 3. 当前仅限于基本数据类型的单值覆盖,无法满足更复杂的逻辑需求。 以上就是有关 `memset` 函数的一些基础介绍及其典型应用场景[^3]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花落已飘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值