作为 C 语言初学者,我们经常需要对一块内存进行复制、移动、填充或比较等操作。虽然 C 语言标准库提供了强大的字符串处理函数(如strcpystrcmp),但它们只能处理以'\0'结尾的字符数组。当我们需要处理任意二进制数据(如结构体、数组)时,就需要用到今天要介绍的四个内存操作函数:memcpymemmovememsetmemcmp


1. memcpy 使用和模拟实现

函数介绍

memcpy 用于从源内存块复制指定数量的字节到目标内存块。它的函数原型如下:

void *memcpy(void *dest, const void *src, size_t n);
  • dest: 指向目标内存块的指针。
  • src: 指向源内存块的指针(const 表示源数据不会被修改)。
  • n: 要复制的字节数。
  • 返回值: 返回指向目标内存块 dest 的指针。

使用示例

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

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

    // 复制前13个字节
    memcpy(dest, src, 13);
    printf("复制后的 dest: %s\n", dest); // 输出: Hello, memcpy!

    int arr1[] = {1, 2, 3, 4, 5};
    int arr2[5] = {0};
    // 复制整个数组
    memcpy(arr2, arr1, sizeof(arr1));
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr2[i]); // 输出: 1 2 3 4 5
    }
    return 0;
}

注意事项

  • memcpy 假设源和目标内存区域不重叠。如果内存区域重叠,行为是未定义的,此时应使用 memmove
  • 它不检查目标缓冲区的大小,因此必须确保目标缓冲区足够大,以容纳 n 个字节,否则会导致缓冲区溢出。

模拟实现

我们可以自己实现一个简单的 memcpy 来理解其工作原理:

void *my_memcpy(void *dest, const void *src, size_t n) {
    // 检查指针有效性
    if (dest == NULL || src == NULL) {
        return NULL;
    }
    // 为了避免修改指针本身,我们用临时指针
    char *d = (char *)dest;
    const char *s = (const char *)src;

    while (n--) {
        *d++ = *s++;
    }
    return dest;
}

2. memmove 使用和模拟实现

函数介绍

memmovememcpy 功能非常相似,都是复制内存。关键区别在于,memmove 可以安全地处理重叠的内存区域。

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

为什么需要 memmove?

想象一下,如果源地址 src 小于目标地址 dest,并且它们的内存区域有重叠。如果我们像 memcpy 那样从低地址向高地址复制,就会覆盖掉还没复制的源数据。memmove 会根据地址关系选择复制方向,从而避免这个问题。

使用示例

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

int main() {
    char str[] = "abcdefghij";
    // 从 str+2 复制 5 个字节到 str+5,内存区域重叠
    memmove(str+5, str+2, 5);
    printf("%s\n", str); // 输出: abcde cdehij
    return 0;
}

模拟实现

void *my_memmove(void *dest, const void *src, size_t n) {
    if (dest == NULL || src == NULL) {
        return NULL;
    }
    char *d = (char *)dest;
    const char *s = (const char *)src;

    // 如果目标地址在源地址之后,且有重叠,从后往前复制
    if (d > s && d < s + n) {
        d += n - 1;
        s += n - 1;
        while (n--) {
            *d-- = *s--;
        }
    } else {
        // 否则从前往后复制
        while (n--) {
            *d++ = *s++;
        }
    }
    return dest;
}

3. memset 函数的使用

函数介绍

memset 用于将一块内存的前 n 个字节设置为指定的值。它通常用于初始化内存。

void *memset(void *ptr, int value, size_t n);
  • ptr: 指向要填充的内存块。
  • value: 要设置的值。虽然是 int 类型,但实际会被转换为 unsigned char 填充到每个字节。
  • n: 要设置的字节数。

使用示例

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

int main() {
    char buffer[10];
    // 将 buffer 的所有字节设置为 'A'
    memset(buffer, 'A', sizeof(buffer));
    for (int i = 0; i < 10; i++) {
        printf("%c ", buffer[i]); // 输出: A A A A A A A A A A
    }
    printf("\n");

    int arr[5];
    // 将数组 arr 清零
    memset(arr, 0, sizeof(arr));
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]); // 输出: 0 0 0 0 0
    }
    return 0;
}

注意事项

  • memset 是按字节填充的。因此,如果你想初始化一个 int 数组为 1,直接使用 memset(arr, 1, sizeof(arr)) 是错误的,它会把每个字节都设为 0x01,导致每个 int 变成 0x01010101(十进制 16843009)。

4. memcmp 函数的使用

函数介绍

memcmp 用于比较两块内存区域的前 n 个字节。它按字节逐位比较。

int memcmp(const void *ptr1, const void *ptr2, size_t n);
  • 返回值:
    • < 0: ptr1 指向的内存块小于 ptr2 指向的内存块。
    • = 0: 两个内存块的前 n 个字节完全相同。
    • > 0: ptr1 指向的内存块大于 ptr2 指向的内存块。

使用示例

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

int main() {
    char str1[] = "Hello";
    char str2[] = "Helium";
    // 比较前3个字节
    int result1 = memcmp(str1, str2, 3);
    if (result1 == 0) {
        printf("str1 和 str2 的前3个字节相同\n"); // 输出此句
    }

    int arr1[] = {1, 2, 3};
    int arr2[] = {1, 2, 4};
    // 比较整个数组
    int result2 = memcmp(arr1, arr2, sizeof(arr1));
    if (result2 < 0) {
        printf("arr1 小于 arr2\n"); // 输出此句
    }
    return 0;
}

总结

函数 功能 关键特点
memcpy 内存复制 不处理重叠区域,速度快
memmove 内存移动 安全处理重叠区域
memset 内存填充 按字节初始化内存
memcmp 内存比较 按字节逐位比较

掌握这四个函数,能让你在处理 C 语言中的内存数据时更加得心应手。它们是构建更复杂数据结构和算法的基础工具。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐