首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【C语言内存函数全面解析】深入了解 memcpy、memmove、memset 和 memcmp 的区别与应用

【C语言内存函数全面解析】深入了解 memcpy、memmove、memset 和 memcmp 的区别与应用

作者头像
Extreme35
发布2025-12-23 18:03:34
发布2025-12-23 18:03:34
20
举报
文章被收录于专栏:DLDL

💡【入门必看】C语言四大内存函数全解析:memcpy、memmove、memset、memcmp

👋 你是否经常被这些“看起来差不多”的内存函数搞糊涂? “memcpy 和 memmove 到底有什么区别?” “memset 填充的是字符还是整数?” “memcmp 为什么比较不出字符串的大小?” 别急,这篇文章我们从最基础的原理、内存图示、代码示例、常见坑与模拟实现一步步讲清楚! 阅读完这篇,你不仅能用,还能“讲给别人听”。


🧠 一、什么是内存函数?为什么要学?

在 C 语言中,内存函数(memory functions) 是直接操作内存字节的函数族,位于 <string.h> 中。 它们不依赖 \0 结束符,也不关心数据类型。

函数

功能

是否允许重叠

返回类型

memcpy()

拷贝内存

❌ 否

void*

memmove()

拷贝(重叠安全)

✅ 是

void*

memset()

填充内存

void*

memcmp()

比较内存

int

🧩 一句话总结: 掌握这四个函数,你才能真正理解指针、数组和内存的本质。


⚙️ 二、memcpy —— 最快的内存搬运工

📘 函数原型
代码语言:javascript
复制
void *memcpy(void *destination, const void *source, size_t num);

memcpy 是 C 语言中用于内存块复制的标准库函数,定义在 <string.h> 中。

  • 它的作用是从源地址 src 开始,连续复制指定字节数 count 到目标地址 dst,适用于任意类型的数据(数组、结构体、缓冲区等)。
  • 该函数按字节级别工作,不依赖 '\0' 结束符,也不会检测内存重叠,因此在源与目标区域重叠时可能导致未定义行为。
📗 功能

source 拷贝 num 个字节到 destination。 不会在遇到 '\0' 时停止,也不判断重叠。

📗 示例
代码语言:javascript
复制
#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;
}

🖨️ 输出结果:

代码语言:javascript
复制
1 2 3 4 5 6 7 8 9 10
🧱 手写实现
  • 模拟实现 memcpy 的核心思想是逐字节复制内存内容。 函数接收目标指针 dst、源指针 src 和要复制的字节数 count。 首先保存目标首地址 ret 以便返回,然后通过 assert 检查指针有效性。接着把 dstsrc 强制转换为 char*,因为在 C 语言中只有 char 类型能安全访问任意对象的每一个字节。 随后使用 while(count--) 循环,从源地址起始位置开始,将每个字节依次写入目标区域,每复制一个字节就让指针前进一位,直到复制完所有字节。
代码语言:javascript
复制
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 —— 处理重叠的“智能搬家工”

📘 函数原型
代码语言:javascript
复制
void *memmove(void *destination, const void *source, size_t num);

它与 memcpy 功能类似,都是将指定数量的字节从源地址 src 复制到目标地址 dst,但 memmove 能正确处理内存区域重叠的情况。 当检测到源和目标区域重叠时,memmove 会自动选择复制方向(从前向后或从后向前),确保数据不会被覆盖。

📗 示例
代码语言:javascript
复制
#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;
}

🖨️ 输出:

代码语言:javascript
复制
1 2 1 2 3 4 5 8 9 10
🧱 模拟实现

模拟实现 memmove 的核心思路是先判定是否存在内存重叠风险,再选择合适的复制方向来避免覆盖源数据:当 dst <= srcdstsrc + count 之外(即两块区域不重叠或 dstsrc 低地址一侧)时,采用前向拷贝(从低地址到高地址);否则说明 dst 落在 src 的后半段,存在覆盖风险,就改用反向拷贝(从高地址到低地址)。这种方向选择是 memmovememcpy 的关键差异,能安全处理重叠区域。

代码语言:javascript
复制
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 语言中最高效的内存初始化工具之一。

代码语言:javascript
复制
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "hello world";
    memset(str, 'x', 6);
    printf("%s", str);
    return 0;
}

🖨️ 输出:

代码语言:javascript
复制
xxxxxxworld

⚖️ 五、memcmp —— 比较二进制内容的裁判

  • 它会从 ptr1ptr2 指向的地址开始,逐字节比较前 num 个字节的内容。 如果两块内存完全相同则返回 0;若第一个不相同的字节中,ptr1 的值大于 ptr2 的值则返回正数,否则返回负数。
  • strcmp 不同,memcmp 不依赖字符串结束符 '\0',因此可用于比较任意类型的二进制数据,如结构体、文件缓冲区等。

它常用于内存校验、排序或验证数据是否一致的底层操作中。

代码语言:javascript
复制
#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;
}

🖨️ 输出:

代码语言:javascript
复制
'DWgaOtP12df0' > 'DWGAOTP12DF0'

🧩 六、四大函数对比总结

函数

功能

是否重叠安全

典型用途

memcpy

内存拷贝

❌ 否

快速复制数据

memmove

内存拷贝(重叠安全)

✅ 是

缓冲区移动

memset

内存填充

初始化数组、结构体

memcmp

内存比较

校验、排序、验证


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 💡【入门必看】C语言四大内存函数全解析:memcpy、memmove、memset、memcmp
    • 🧠 一、什么是内存函数?为什么要学?
    • ⚙️ 二、memcpy —— 最快的内存搬运工
      • 📘 函数原型
      • 📗 功能
      • 📗 示例
      • 🧱 手写实现
    • 🧩 三、memmove —— 处理重叠的“智能搬家工”
      • 📘 函数原型
      • 📗 示例
      • 🧱 模拟实现
    • 🧱 四、memset —— 内存初始化好帮手
    • ⚖️ 五、memcmp —— 比较二进制内容的裁判
    • 🧩 六、四大函数对比总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档