1. 内存操作函数概述

   所需头文件:

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

2. memcpy - 内存复制函数

2.1 函数原型与基本用法

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

参数:dest: 目标内存地址

    src: 源内存地址 

    n: 要复制的字节数

2.2 使用示例

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

2.3 重要特性

  • 不处理内存重叠:如果源和目标内存区域重叠,行为未定义

  • 高效复制:通常使用硬件加速指令实现

  • 按字节复制:不考虑数据类型,纯粹的内存块复制

3. memmove - 安全的内存移动函数

3.1 函数原型

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

参数:dest :指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。

          src :指向要复制的数据源,类型强制转换为 void* 指针。

          n :要被复制的字节数。

使用实例:

int main() {
    char str[] = "memmove example";
    printf("%s\n", str);
    memmove(str + 3, str, 7); // 安全处理重叠区域
    printf("Aftermemmove: %s\n", str); // 输出: memmemmoveample
    return 0;
}

3.2 与memcpy的区别

两者的核心功能都是将源内存块(src)中的 n 个字节复制到目标内存块(dest),但设计目标不同:

memcpy:追求复制效率,是一种 "快速但不安全" 的内存复制方案,不处理内存重叠情况memmove:追求复制安全性,是一种 "稳健但可能稍慢" 的内存复制方案,专门处理内存重叠情况

"内存重叠" 指的是源内存块和目标内存块在地址空间上有部分或完全重叠的情况(例如:dest = src + 2)。

 3.3 有内存重叠的情况

当存在内存重叠时,两者的行为差异显著:

情况 A:目标地址在源地址之后(dest > src)

vs2022上没有这种差距,其他平台暂且不知,但这种情况依然存在。

情况 B:目标地址在源地址之前(dest < src)

vs2022上没有这种差距,其他平台暂且不知,但这种情况依然存在。

3.4 实现原理差异

两者处理方式的不同源于内部实现策略:

  • memcpy实现:通常采用从低地址到高地址的顺序直接复制(字节逐个搬运),效率较高,但在内存重叠时会出现数据覆盖问题。

  • memmove实现:会先判断内存是否重叠及重叠方向:

              若不重叠:直接按正常顺序复制(同 memcpy)                                                                        若dest > src(目标在源之后):从高地址向低地址复制(避免覆盖未读取的源数据)            若dest < src(目标在源之前):从低地址向高地址复制

关键区别:memmove会检查内存重叠情况,并采取适当的复制策略(从前向后或从后向前复制),确保数据完整性。

3.5 实际测试

在vs2022测试中发现不论怎么测试俩者输出结果都相同,说明vs2022对memcpy做了优化处理,但其他平台不一定输出相同结果,因此考虑安全性还是得综合考虑是否有内存重叠的情况。

4. memset - 内存设置函数

4.1 函数原型

void *memset(void *s, int c, size_t n);

参数:s: 目标内存地址

    c: 要设置的值(转换为unsigned char)

    n: 要设置的字节数

4.2 使用示例

int main() {
    char buffer[10];
    memset(buffer, 'A', 9); // 设置前9个字节为'A'
    buffer[9] = '\0'; // 添加字符串结束符
    printf("Buffer: %s\n", buffer); // 输出: AAAAAAAAA 
    int arr[5];
    memset(arr, 0, sizeof(arr)); // 数组清零
    for(int i = 0; i < 5; i++) {
        printf("%d ", arr[i]); // 输出: 0 0 0 0 0
    }
    return 0;
}

4.3 注意事项

  • 字节级操作:memset按字节操作,不适合初始化非字符数组为特定值

  • 清零利器:最适合用于内存块清零操作

5. memcmp - 内存比较函数

5.1 函数原型

int memcmp(const void *s1, const void *s2, size_t n);

参数:s1 : 指向内存块的指针。

          s2 : 指向内存块的指针。

          n :要被比较的字节数。

返回值为:0:两个内存块完全相同

                 负数:s1小于s2

                 正数:s1大于s2

5.2 使用示例

int main() {
    char str1[] = "Hello";
    char str2[] = "Hello";
    char str3[] = "World";
    int result1 = memcmp(str1, str2, 5); // 返回0
    int result2 = memcmp(str1, str3, 5); // 返回负数
    printf("Compare result1: %d\n", result1);
    printf("Compare result2: %d\n", result2);
    return 0;
}

6. memchr - 内存字符搜索函数

6.1 函数原型

void *memchr(const void *s, int c, size_t n);

参数:s : 指向要执行搜索的内存块。

           c : 以 int 形式传递的值,但是函数在每次字节搜索时是使用该值的无符号字符形式。

           n : 要被分析的字节数。

6.2 使用示例

int main() {
    char str[] = "Search for character";
    char *result = memchr(str, 'c', strlen(str));
    if(result != NULL) {
        printf("Found at position: %ld\n", result - str); // 输出: 2
        printf("String from found: %s\n", result); // 输出: character
    }
    return 0;
}

7. 综合应用实例

7.1 结构体操作

typedef struct {
    int id;
    char name[20];
    float score;
} Student;
int main() {
    Student s1 = {1, "Alice", 95.5};
    Student s2;
    memcpy(&s2, &s1, sizeof(Student)); // 结构体复制
    printf("Copied student: %d, %s, %.1f\n", s2.id, s2.name, s2.score);
    memset(&s1, 0, sizeof(Student)); // 结构体清零
    printf("After memset: %d, %s, %.1f\n", s1.id, s1.name, s1.score); 
    return 0;
}

7.2 内存块管理

int main() {
    void *memory = malloc(100); // 分配100字节
    if(memory == NULL) return 1;
    memset(memory, 0, 100); // 清零初始化
    memcpy(memory, "Hello World", 12); // 复制字符串
    printf("Memory content: %s\n", (char*)memory);    
    char *found = memchr(memory, 'W', 100);// 查找字符
    if(found) printf("Found 'W' at offset: %ld\n", found - (char*)memory);  
    free(memory);
    return 0;
}

8. 性能优化技巧

8.1 批量操作优于循环

// 不推荐
for(int i = 0; i < 1000; i++) {
    arr[i] = 0;
}
// 推荐
memset(arr, 0, sizeof(arr));

8.2 对齐访问

mem系列函数通常针对对齐的内存访问进行了优化,确保数据按合适的边界对齐可以提高性能。

9. 安全注意事项

9.1 缓冲区溢出防护

int main() {
    char dest[10];
    char src[20] = "This is too long";   
    // 安全复制:不超过目标缓冲区大小
    size_t copy_size = sizeof(dest) - 1; // 保留空间给结束符
    memcpy(dest, src, copy_size);
    dest[copy_size] = '\0';   
    printf("Safe copy: %s\n", dest);
    return 0;
}

10. 总结与记忆要点

函数 用途 关键特性 使用场景
memcpy 内存复制 不处理重叠 不重叠内存块复制
memmove 内存移动 安全处理重叠 重叠内存区域操作
memset 内存设置 字节级填充 内存初始化、清零
memcmp 内存比较 二进制比较 内存块内容比较
memchr 字符搜索 字节搜索 在内存中查找特定值

核心记忆点

  1. memcpy速度最快,但要求内存不重叠

  2. memmove最安全,可处理任何情况的内存复制

  3. memset效率最高的内存初始化方法

  4. memcmp进行二进制比较,不考虑数据类型

  5. 所有函数都按字节操作,与数据类型无关

Logo

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

更多推荐