目录

一、memcpy函数的使用和模拟实现(内存拷贝)

1.函数原型与特点

2.使用示例

3.注意事项与常见错误

4.模拟实现

二、memmove函数的使用和模拟实现(内存拷贝)

1.函数原型与特点

2.使用示例

3.注意事项与常见错误

4.模拟实现

5.与memcpy、strcpy、strncpy的区别

三、memset函数的使用(内存置数)

1.函数原型与特点

2.使用示例

3.注意事项与常见错误

4.与calloc的区别

四、memcmp函数的使用(内存比较)

1.函数原型与特点

2.使用示例

3.注意事项与常见错误


一、memcpy函数的使用和模拟实现(内存拷贝)

1.函数原型与特点

原型:

void *memcpy(void *dest, const void *src, size_t n);
  • 头文件<string.h>

  • 参数:

    • dest - 目标内存地址

    • src - 源内存地址

    • n - 要复制的字节数

  • 返回值: 返回目标内存地址的指针(即dest的地址)

  • 功能: 从源内存地址复制n个字节到目标内存地址

特点:

  • 不知道数据的类型,只是逐字节复制(char*)
  • 源和目标内存不能重叠(如果重叠,使用memmove)
  • 复制指定的字节数,不会自动添加结束符\0

2.使用示例

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

int main() {
    // 示例1: 复制整型数组
    int src_arr[5] = {1, 2, 3, 4, 5};
    int dest_arr[5];
    
    memcpy(dest_arr, src_arr, sizeof(src_arr));
    
    printf("Copied array: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", dest_arr[i]);
    }
    printf("\n");
    
    // 示例2: 复制字符串
    char src_str[] = "Hello, World!";
    char dest_str[50];
    
    memcpy(dest_str, src_str, strlen(src_str) + 1);  // +1 复制\0
    printf("Copied string: %s\n", dest_str);
    
    // 示例3: 复制部分数据
    int numbers[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int partial_copy[5];
    
    memcpy(partial_copy, numbers + 3, 5 * sizeof(int));  // 复制索引3开始的5个整数
    printf("Partial copy: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", partial_copy[i]);
    }
    printf("\n");
    
    return 0;
}

3.注意事项与常见错误

/****************************** 1.源和目标内存不允许重叠 ******************************/
// 错误:源和目标内存重叠
char str[20] = "Hello, World!";
memcpy(str + 7, str, 7);  // 未定义行为!

// 正确:使用memmove处理重叠内存
memmove(str + 7, str, 7);  // 正确


/****************************** 2.目标缓冲区不能太小  ******************************/
// 错误:目标缓冲区太小
char src[100] = "This is a long string...";
char dest[10];
memcpy(dest, src, sizeof(src));  // 缓冲区溢出!

// 正确:确保目标缓冲区足够大
char dest_safe[100];
memcpy(dest_safe, src, sizeof(src));  // 正确


/****************************** 3.memcpy不会补充\0 ******************************/
// 错误:复制字符串时忘记\0
char src[] = "Hello";
char dest[10];
memcpy(dest, src, strlen(src));  // 只复制了5个字节,没有\0
// dest现在不是有效的C字符串

// 正确:包括\0
memcpy(dest, src, strlen(src) + 1);  // +1 复制\0

4.模拟实现

/***************************** 方法一:逐字符复制 *****************************/
void *my_memcpy1(void *dest, const void *src, size_t n) {
    if (dest == NULL || src == NULL || n == 0) {
        return dest;
    }
    
    // 转换为字节指针
    unsigned char *d = (unsigned char *)dest;
    const unsigned char *s = (const unsigned char *)src;
    
    // 逐字节复制
    for (size_t i = 0; i < n; i++) {
        d[i] = s[i];
    }
    
    return dest;
}

/***************************** 方法二:使用指针 *****************************/
void *my_memcpy2(void *dest, const void *src, size_t n) {
    if (dest == NULL || src == NULL || n == 0) {
        return dest;
    }
    
    unsigned char *d = (unsigned char *)dest;
    const unsigned char *s = (const unsigned char *)src;
    
    // 使用指针而不是索引
    while (n--) {
        *d++ = *s++;
    }
    
    return dest;
}

二、memmove函数的使用和模拟实现(内存拷贝)

1.函数原型与特点

原型:

void *memmove(void *dest, const void *src, size_t n);
  • 参数:

    • dest - 目标内存地址

    • src - 源内存地址

    • n - 要复制的字节数

  • 返回值: 返回目标内存地址的指针(即dest的地址)

  • 功能: 从源内存地址复制n个字节到目标内存地址,可以正确处理内存重叠的情况

特点:

  • 允许内存重叠
  • 以字节为单位复制
  • 不会补充\0

2.使用示例

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

int main() {
    // 示例1: 非重叠内存复制(与memcpy相同)
    int src_arr[5] = {1, 2, 3, 4, 5};
    int dest_arr[5];
    
    memmove(dest_arr, src_arr, sizeof(src_arr));
    
    printf("Copied array: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", dest_arr[i]);
    }
    printf("\n");
    
    // 示例2: 重叠内存复制(memmove的关键特性)
    char str[20] = "Hello, World!";
    printf("Before memmove: %s\n", str);
    
    // 将字符串的前6个字符复制到从第7个字符开始的位置
    memmove(str + 7, str, 6);
    printf("After memmove:  %s\n", str);  // 输出: Hello, Hello!
    
    // 示例3: 反向重叠(目标在源之前)
    char str2[20] = "Hello, World!";
    memmove(str2, str2 + 7, 6);
    printf("Backward overlap: %s\n", str2);  // 输出: World!World!
    
    return 0;
}

3.注意事项与常见错误

/****************************** 1.是以字节为单位复制 ******************************/
// 错误:使用错误的长度
int src_arr[5] = {1, 2, 3, 4, 5};
int dest_arr[5];

// 错误:忘记乘以元素大小
memmove(dest_arr, src_arr, 5);  // 只复制了5个字节,不是5个整数!

// 正确:使用sizeof
memmove(dest_arr, src_arr, 5 * sizeof(int));  // 正确
// 或者
memmove(dest_arr, src_arr, sizeof(src_arr));  // 正确


/****************************** 2.目标缓冲区不能太小  ******************************/
// 错误:目标缓冲区太小
char src[100] = "This is a long string...";
char dest[10];
memmove(dest, src, sizeof(src));  // 缓冲区溢出!

// 正确:确保目标缓冲区足够大
char dest_safe[100];
memmove(dest_safe, src, sizeof(src));  // 正确

// 或者只复制安全数量的字节
size_t safe_size = sizeof(dest_safe) < sizeof(src) ? sizeof(dest_safe) : sizeof(src);
memmove(dest_safe, src, safe_size);


/****************************** 3.memmove不会补充\0 ******************************/
// 错误:复制字符串时忘记\0
char src[] = "Hello";
char dest[10];
memmove(dest, src, strlen(src));  // 只复制了5个字节,没有\0
// dest现在不是有效的C字符串

// 正确:包括\0
memmove(dest, src, strlen(src) + 1);  // +1 复制\0

4.模拟实现

/***************************** 方法一:检查重叠并选择复制方向 *****************************/
void *my_memmove1(void *dest, const void *src, size_t n) {
    if (dest == NULL || src == NULL || n == 0) {
        return dest;
    }
    
    unsigned char *d = (unsigned char *)dest;
    const unsigned char *s = (const unsigned char *)src;
    
    // 检查内存是否重叠
    if (s < d && s + n > d) {
        // 重叠且目标在源之后,从后往前复制
        for (size_t i = n; i > 0; i--) {
            d[i - 1] = s[i - 1];
        }
    } else {
        // 不重叠,或目标在源之前,从前往后复制
        for (size_t i = 0; i < n; i++) {
            d[i] = s[i];
        }
    }
    
    return dest;
}

/***************************** 方法一:使用指针 *****************************/
void *my_memmove2(void *dest, const void *src, size_t n) {
    if (dest == NULL || src == NULL || n == 0) {
        return dest;
    }
    
    unsigned char *d = (unsigned char *)dest;
    const unsigned char *s = (const unsigned char *)src;
    
    if (s < d && s + n > d) {
        // 重叠且目标在源之后,从后往前复制
        d += n;
        s += n;
        while (n--) {
            *(--d) = *(--s);
        }
    } else {
        // 不重叠,或目标在源之前,从前往后复制
        while (n--) {
            *d++ = *s++;
        }
    }
    
    return dest;
}

5.与memcpy、strcpy、strncpy的区别

特性 memcpy memmove strcpy strncpy
用途 内存块复制 内存块复制(可重叠) 字符串复制 字符串复制(指定长度)
停止条件 复制n个字节 复制n个字节 遇到\0停止 复制n个字符或遇到\0
处理重叠 未定义行为 正确处理 未定义行为 未定义行为
添加\0 从不 从不 总是 只有源长度<n时
性能 通常最快 稍慢(检查重叠) 较快 较慢

三、memset函数的使用(内存置数)

1.函数原型与特点

原型:

void *memset(void *ptr, int value, size_t num);
  • 参数:

    • ptr - 指向要填充的内存块的指针

    • value - 要设置的值(以int形式传递,但函数会将该值转换为unsigned char)

    • num - 要填充的字节数

  • 返回值: 返回指向内存块ptr的指针

  • 功能: 将ptr指向的内存块的前num个字节设置为value的值

特点:

  • 以字节为单位进行填充,不关心数据类型
  • 即使传递负数,也会被转换为unsigned char
  • 常用于清零和初始化
  • 不会自动添加结束符\0,如果用作字符串,需要手动添加\0

2.使用示例

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

int main() {
    // 示例1: 将数组初始化为0
    int arr[10];
    memset(arr, 0, sizeof(arr));
    
    printf("Array initialized to 0: ");
    for (int i = 0; i < 10; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    // 示例2: 将字符串数组初始化为特定字符
    char str[20];
    memset(str, 'A', 19);
    str[19] = '\0';  // 手动添加结束符
    printf("String filled with 'A': %s\n", str);
    
    // 示例3: 将结构体清零
    struct Point {
        int x;
        int y;
        char label[10];
    } point;
    
    memset(&point, 0, sizeof(point));
    printf("Point structure cleared: x=%d, y=%d, label='%s'\n", 
           point.x, point.y, point.label);
    
    return 0;
}

3.注意事项与常见错误

/****************************** 1.memset是以字节为单位置数,而不是以元素为单位 ******************************/
// 错误:试图将整型数组初始化为1
int arr[10];
memset(arr, 1, sizeof(arr));  // 每个字节设为1,不是每个整数设为1
// arr[0]将是0x01010101(16843009),而不是1

// 正确:使用循环初始化整型数组
for (int i = 0; i < 10; i++) {
    arr[i] = 1;
}

// 正确:使用memset清零
memset(arr, 0, sizeof(arr));  // 所有字节设为0,每个整数为0


/****************************** 2.用于字符串时不能忘记手动设置\0 ******************************/
// 错误:填充字符串但忘记结束符
char buffer[10];
memset(buffer, 'A', 10);  // 10个'A',没有\0
printf("%s\n", buffer);   // 未定义行为,可能打印乱码

// 正确:预留位置给\0
memset(buffer, 'A', 9);
buffer[9] = '\0';  // 现在有9个'A'和一个\0
printf("%s\n", buffer);  // 正确


/****************************** 3.用于负数时会将其转为无符号类型 ******************************/
// memset将值转换为unsigned char,所以会有截断
char buf[10];
memset(buf, 300, 10);  // 300被截断为44(300 % 256)
memset(buf, -1, 10);   // -1被转换为255(0xFF)

// 更清晰的写法
memset(buf, 0xFF, 10);  // 明确使用无符号值

4.与calloc的区别

特性 memset calloc
用途 设置已分配内存的值 分配并清零内存
参数 已分配的内存指针、值、字节数 元素数量、元素大小
内存来源 已有内存块 从堆分配新内存
清零效率 可以清零任意内存 专门用于清零新分配的内存
灵活性 可以设置为任意值 只能设置为0

四、memcmp函数的使用(内存比较)

1.函数原型与特点

原型:

int memcmp(const void *ptr1, const void *ptr2, size_t num);
  • 参数:

    • ptr1 - 指向第一个内存块的指针

    • ptr2 - 指向第二个内存块的指针

    • num - 要比较的字节数

  • 返回值:

    • 如果ptr1 < ptr2,返回负数

    • 如果ptr1 > ptr2,返回正数

    • 如果ptr1 == ptr2,返回0

  • 功能: 比较两个内存块的前num个字节

特点:

  • 按字节比较,不关心数据类型
  • 与strcmp不同,memcmp不会在\0处停止
  • 可以比较任意内存区域
  • 大小相同返回0,大小不同返回值不一定是-1或1

2.使用示例

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

int main() {
    // 示例1: 比较两个字符串
    char str1[] = "Hello";
    char str2[] = "Hello";
    char str3[] = "Hello, World!";
    
    int result1 = memcmp(str1, str2, 5);   // 比较前5个字符
    printf("memcmp(\"Hello\", \"Hello\", 5) = %d\n", result1);  // 输出: 0
    
    int result2 = memcmp(str1, str3, 5);   // 比较前5个字符
    printf("memcmp(\"Hello\", \"Hello, World!\", 5) = %d\n", result2);  // 输出: 0
    
    int result3 = memcmp(str1, str3, 7);   // 比较前7个字符
    printf("memcmp(\"Hello\", \"Hello, World!\", 7) = %d\n", result3);  // 输出: 负数
    
    // 示例2: 比较整数数组
    int arr1[] = {1, 2, 3, 4, 5};
    int arr2[] = {1, 2, 3, 4, 5};
    int arr3[] = {1, 2, 3, 4, 6};
    
    int result4 = memcmp(arr1, arr2, 5 * sizeof(int));
    printf("Array comparison (equal): %d\n", result4);  // 输出: 0
    
    int result5 = memcmp(arr1, arr3, 5 * sizeof(int));
    printf("Array comparison (different): %d\n", result5);  // 输出: 负数
    
    // 示例3: 比较部分内存
    unsigned char data1[] = {0x01, 0x02, 0x03, 0x04, 0x05};
    unsigned char data2[] = {0x01, 0x02, 0x03, 0x04, 0x06};
    
    int result6 = memcmp(data1, data2, 4);  // 比较前4个字节
    printf("Partial comparison (equal): %d\n", result6);  // 输出: 0
    
    int result7 = memcmp(data1, data2, 5);  // 比较所有5个字节
    printf("Full comparison (different): %d\n", result7);  // 输出: 负数
    
    return 0;
}

3.注意事项与常见错误

/****************************** 1.字节顺序问题 ******************************/
// 在不同字节序的机器上,memcmp结果可能不同
uint32_t a = 0x12345678;
uint8_t b[4] = {0x12, 0x34, 0x56, 0x78};

// 在大端机器上:memcmp(&a, b, 4) == 0
// 在小端机器上:memcmp(&a, b, 4) != 0

// 解决方案:使用网络字节序(大端)或手动比较


/****************************** 2.比较特殊的数据类型可能无意义 ******************************/
// 注意:比较不同类型的数据可能没有意义
int a = 1;
float b = 1.0;
int result = memcmp(&a, &b, sizeof(int));  // 无意义,比较位模式

// 浮点数的特殊值可能有问题
float f1 = -0.0;
float f2 = 0.0;
int r = memcmp(&f1, &f2, sizeof(float));  // 可能返回非0,尽管数学上相等

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐