在这里插入图片描述
🏠 个人主页: 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 检查指针有效性。接着把 dstsrc 强制转换为 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 <= srcdstsrc + count 之外(即两块区域不重叠或 dstsrc 低地址一侧)时,采用前向拷贝(从低地址到高地址);否则说明 dst 落在 src 的后半段,存在覆盖风险,就改用反向拷贝(从高地址到低地址)。这种方向选择是 memmovememcpy 的关键差异,能安全处理重叠区域。

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 —— 比较二进制内容的裁判

  • 它会从 ptr1ptr2 指向的地址开始,逐字节比较前 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 内存比较 校验、排序、验证

在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐