【C语言】入门必看四大内存函数全解析:memcpy、memmove、memset、memcmp
你是否总是被 C 语言中的内存函数弄混?memcpy 和 memmove 究竟有何不同?memset 是如何高效初始化内存的?memcmp 为什么能比字符串比较更灵活?别担心!这篇文章将从原理、代码实现、常见问题到优化技巧,全面剖析这四个内存函数的使用场景和注意事项。阅读完,你不仅能熟练运用这些函数,还能理解它们在 C 语言中的底层操作。

🏠 个人主页: Marathon_X
📚 个人专栏:
| 专栏名称 | 专栏主题简述 |
|---|---|
| 《C语言》 | C语言基础、语法解析与实战应用 |
| 《数据结构》 | 线性表、树、图等核心数据结构详解 |
| 《题解思维》 | 算法思路、解题技巧与高效编程实践 |
| 《排序详解》 | 算法思路、解题技巧与高效编程实践 |

目录
👋 你是否经常被这些“看起来差不多”的内存函数搞糊涂?
“memcpy 和 memmove 到底有什么区别?”
“memset 填充的是字符还是整数?”
“memcmp 为什么比较不出字符串的大小?”别急,这篇文章我们从最基础的原理、内存图示、代码示例、常见坑与模拟实现一步步讲清楚!
阅读完这篇,你不仅能用,还能“讲给别人听”。
🧠 一、什么是内存函数?为什么要学?
在 C 语言中,内存函数(memory functions) 是直接操作内存字节的函数族,位于 <string.h> 中。
它们不依赖 \0 结束符,也不关心数据类型。
| 函数 | 功能 | 是否允许重叠 | 返回类型 |
|---|---|---|---|
memcpy() |
拷贝内存 | ❌ 否 | void* |
memmove() |
拷贝(重叠安全) | ✅ 是 | void* |
memset() |
填充内存 | — | void* |
memcmp() |
比较内存 | — | int |
🧩 一句话总结:
掌握这四个函数,你才能真正理解指针、数组和内存的本质。
⚙️ 二、memcpy —— 最快的内存搬运工
📘 函数原型
void *memcpy(void *destination, const void *source, size_t num);
memcpy 是 C 语言中用于内存块复制的标准库函数,定义在 <string.h> 中。
- 它的作用是从源地址
src开始,连续复制指定字节数count到目标地址dst,适用于任意类型的数据(数组、结构体、缓冲区等)。 - 该函数按字节级别工作,不依赖
'\0'结束符,也不会检测内存重叠,因此在源与目标区域重叠时可能导致未定义行为。
📗 功能
从 source 拷贝 num 个字节到 destination。
不会在遇到 '\0' 时停止,也不判断重叠。
📗 示例
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = {1,2,3,4,5,6,7,8,9,10};
int arr2[10] = {0};
memcpy(arr2, arr1, 20); // 复制 20 字节(假设 int=4 字节)
for (int i = 0; i < 10; i++)
printf("%d ", arr2[i]);
return 0;
}
🖨️ 输出结果:
1 2 3 4 5 6 7 8 9 10
🧱 手写实现
- 模拟实现
memcpy的核心思想是逐字节复制内存内容。 函数接收目标指针dst、源指针src和要复制的字节数count。 首先保存目标首地址ret以便返回,然后通过assert检查指针有效性。接着把dst与src强制转换为char*,因为在 C 语言中只有char类型能安全访问任意对象的每一个字节。 随后使用while(count--)循环,从源地址起始位置开始,将每个字节依次写入目标区域,每复制一个字节就让指针前进一位,直到复制完所有字节。
void *my_memcpy(void *dst, const void *src, size_t count)
{
void *ret = dst;
assert(dst && src);
while (count--)
{
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
return ret;
}
- 这一实现体现了
memcpy的本质特征:按字节操作、与数据类型无关、不依赖字符串结束符'\0'。 它能高效复制任意类型的数据块(如数组、结构体等),但不能处理重叠内存,否则源数据会被覆盖导致结果未定义。 这也是与memmove的根本区别。
⚠️ 注意: memcpy 无法处理重叠区域。遇到重叠请使用 memmove。
🧩 三、memmove —— 处理重叠的“智能搬家工”
📘 函数原型
void *memmove(void *destination, const void *source, size_t num);
它与 memcpy 功能类似,都是将指定数量的字节从源地址 src 复制到目标地址 dst,但 memmove 能正确处理内存区域重叠的情况。 当检测到源和目标区域重叠时,memmove 会自动选择复制方向(从前向后或从后向前),确保数据不会被覆盖。
📗 示例
#include <stdio.h>
#include <string.h>
int main() {
int arr[] = {1,2,3,4,5,6,7,8,9,10};
memmove(arr + 2, arr, 20); // arr+2 与 arr 重叠
for (int i = 0; i < 10; i++)
printf("%d ", arr[i]);
return 0;
}
🖨️ 输出:
1 2 1 2 3 4 5 8 9 10
🧱 模拟实现
模拟实现 memmove 的核心思路是先判定是否存在内存重叠风险,再选择合适的复制方向来避免覆盖源数据:当 dst <= src 或 dst 在 src + count 之外(即两块区域不重叠或 dst 在 src 低地址一侧)时,采用前向拷贝(从低地址到高地址);否则说明 dst 落在 src 的后半段,存在覆盖风险,就改用反向拷贝(从高地址到低地址)。这种方向选择是 memmove 与 memcpy 的关键差异,能安全处理重叠区域。
void *my_memmove(void *dst, const void *src, size_t count)
{
void *ret = dst;
if (dst <= src || (char *)dst >= ((char *)src + count))
{
while (count--)
{
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
}
else
{
dst = (char *)dst + count - 1;
src = (char *)src + count - 1;
while (count--)
{
*(char *)dst = *(char *)src;
dst = (char *)dst - 1;
src = (char *)src - 1;
}
}
return ret;
}
实现上将指针统一强转为 char*,以逐字节复制,保证对任意对象表示的合法访问;每次复制后移动指针并递减 count,直到复制完成。函数入口处把 dst 备份到 ret,最终按标准约定返回目标起始地址,便于链式或后续使用。整体时间复杂度 O(n)、空间 O(1),适合通用场景。
🧱 四、memset —— 内存初始化好帮手
- 它的作用是将从指定地址
ptr开始的连续num个字节,全部设置为给定的值value(按字节填充)。
常用于数组、结构体或缓冲区的快速清零、初始化等操作。 - 需要注意的是,
memset按 字节 工作,并不会把整数数组设为某个具体数字(例如memset(arr, 1, sizeof(arr))只是将每个字节设为 0x01)。
正确使用时,它是 C 语言中最高效的内存初始化工具之一。
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "hello world";
memset(str, 'x', 6);
printf("%s", str);
return 0;
}
🖨️ 输出:
xxxxxxworld
⚖️ 五、memcmp —— 比较二进制内容的裁判
- 它会从
ptr1和ptr2指向的地址开始,逐字节比较前num个字节的内容。
如果两块内存完全相同则返回0;若第一个不相同的字节中,ptr1的值大于ptr2的值则返回正数,否则返回负数。 - 与
strcmp不同,memcmp不依赖字符串结束符'\0',因此可用于比较任意类型的二进制数据,如结构体、文件缓冲区等。
它常用于内存校验、排序或验证数据是否一致的底层操作中。
#include <stdio.h>
#include <string.h>
int main() {
char buffer1[] = "DWgaOtP12df0";
char buffer2[] = "DWGAOTP12DF0";
int n = memcmp(buffer1, buffer2, sizeof(buffer1));
if (n > 0)
printf("'%s' > '%s'\n", buffer1, buffer2);
else if (n < 0)
printf("'%s' < '%s'\n", buffer1, buffer2);
else
printf("'%s' == '%s'\n", buffer1, buffer2);
return 0;
}
🖨️ 输出:
'DWgaOtP12df0' > 'DWGAOTP12DF0'
🧩 六、四大函数对比总结
| 函数 | 功能 | 是否重叠安全 | 典型用途 |
|---|---|---|---|
| memcpy | 内存拷贝 | ❌ 否 | 快速复制数据 |
| memmove | 内存拷贝(重叠安全) | ✅ 是 | 缓冲区移动 |
| memset | 内存填充 | — | 初始化数组、结构体 |
| memcmp | 内存比较 | — | 校验、排序、验证 |

更多推荐
所有评论(0)